在 .NET 中测试 float 的性能时,我偶然发现了一个奇怪的情况:对于某些值,乘法似乎比正常情况慢得多。这是测试用例:
using System;
using System.Diagnostics;
namespace NumericPerfTestCSharp {
class Program {
static void Main() {
Benchmark(() => float32Multiply(0.1f), "\nfloat32Multiply(0.1f)");
Benchmark(() => float32Multiply(0.9f), "\nfloat32Multiply(0.9f)");
Benchmark(() => float32Multiply(0.99f), "\nfloat32Multiply(0.99f)");
Benchmark(() => float32Multiply(0.999f), "\nfloat32Multiply(0.999f)");
Benchmark(() => float32Multiply(1f), "\nfloat32Multiply(1f)");
}
static void float32Multiply(float param) {
float n = 1000f;
for (int i = 0; i < 1000000; ++i) {
n = n * param;
}
// Write result to prevent the compiler from optimizing the entire method away
Console.Write(n);
}
static void Benchmark(Action func, string message) {
// warm-up call
func();
var sw = Stopwatch.StartNew();
for (int i = 0; i < 5; ++i) {
func();
}
Console.WriteLine(message + " : {0} ms", sw.ElapsedMilliseconds);
}
}
}
结果:
float32Multiply(0.1f) : 7 ms
float32Multiply(0.9f) : 946 ms
float32Multiply(0.99f) : 8 ms
float32Multiply(0.999f) : 7 ms
float32Multiply(1f) : 7 ms
为什么 param = 0.9f 的结果如此不同?
测试参数:.NET 4.5,发布版本,代码优化开启,x86,未附加调试器。
最佳答案
正如其他人所提到的,当涉及次正规浮点值时,各种处理器不支持正常速度计算。这要么是设计缺陷(如果该行为会损害您的应用程序或带来其他麻烦),要么是功能(如果您更喜欢更便宜的处理器或通过不使用门来实现这项工作而启用的硅的替代使用)。
理解为什么在 .5 处有一个转换是很有启发性的:
假设您要乘以 p。最终,该值变得非常小以至于结果是某个次正规值(在 32 位 IEEE 二进制 float 中低于 2-126)。然后乘法变慢。随着你继续乘法,这个值不断减小,最后达到 2-149,这是可以表示的最小正数。现在,当你乘以 p 时,确切的结果当然是 2-149p,它介于 0 和 2- 149,这是两个最接近的可表示值。机器必须对结果进行舍入并返回这两个值之一。
哪一个?如果 p 小于 ½,则 2-149p 比 2-149 更接近 0,所以机器返回 0。然后你不再使用次正规值,乘法又很快了。如果 p 大于 ½,则 2-149p 比 0 更接近 2-149,所以机器返回 2-149,你继续使用次正规值,乘法仍然很慢。如果 p 恰好是 ½,则舍入规则表示使用其有效数(小数部分)的低位为零的值,即零 (2-149其低位为 1)。
您报告 .99f 显示速度很快。这应该以缓慢的行为结束。也许您发布的代码不完全是您使用 .99f 测量快速性能的代码?也许起始值或迭代次数已更改?
有多种方法可以解决此问题。一是硬件具有指定将使用或获得的任何次正规值更改为零的模式设置,称为“非正规化为零”或“刷新为零”模式。我不使用 .NET,因此无法建议您如何在 .NET 中设置这些模式。
另一种方法是每次添加一个微小的值,例如
n = (n+e) * param;
其中 e 至少为 2-126/param。注意2-126/param应该计算向上舍入,除非你能保证n足够大(n+e) * param 不会产生次正常值。这也假定 n 不是负数。这样做的作用是确保计算值始终足够大以处于正常范围内,绝不会低于正常范围。
以这种方式添加 e 当然会改变结果。但是,例如,如果您正在处理具有某些回声效果(或其他过滤器)的音频,则 e 的值太小而不会导致人类收听音频时可观察到的任何效果。它可能太小而不会在生成音频时导致硬件行为发生任何变化。
关于c# - float 的乘法性能不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13964606/
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio
在添加一些空格以使代码更具可读性时(与上面的代码对齐),我遇到了这个:classCdefx42endendm=C.new现在这将给出“错误数量的参数”:m.x*m.x这将给出“语法错误,意外的tSTAR,期待$end”:2/m.x*m.x这里的解析器到底发生了什么?我使用Ruby1.9.2和2.1.5进行了测试。 最佳答案 *用于运算符(42*42)和参数解包(myfun*[42,42])。当你这样做时:m.x*m.x2/m.x*m.xRuby将此解释为参数解包,而不是*运算符(即乘法)。如果您不熟悉它,参数解包(有时也称为“spl
我正在使用Ruby解决一些ProjectEuler问题,特别是这里我要讨论的问题25(Fibonacci数列中包含1000位数字的第一项的索引是多少?)。起初,我使用的是Ruby2.2.3,我将问题编码为:number=3a=1b=2whileb.to_s.length但后来我发现2.4.2版本有一个名为digits的方法,这正是我需要的。我转换为代码:whileb.digits.length当我比较这两种方法时,digits慢得多。时间./025/problem025.rb0.13s用户0.02s系统80%cpu0.190总计./025/problem025.rb2.19s用户0.0
我正在寻找一个用ruby演示计时器的在线示例,并发现了下面的代码。它按预期工作,但这个简单的程序使用30Mo内存(如Windows任务管理器中所示)和太多CPU有意义吗?非常感谢deftime_blockstart_time=Time.nowThread.new{yield}Time.now-start_timeenddefrepeat_every(seconds)whiletruedotime_spent=time_block{yield}#Tohandle-vesleepinteravalsleep(seconds-time_spent)iftime_spent
有没有人用ruby解决这个问题:假设我们有:a=8.1999999我们想将它四舍五入为2位小数,即8.20,然后乘以1,000,000得到8,200,000我们是这样做的;(a.round(2)*1000000).to_i但是我们得到的是8199999,为什么?奇怪的是,如果我们乘以1000、100000或10000000而不是1000000,我们会得到正确的结果。有人知道为什么吗?我们正在使用ruby1.9.2并尝试使用1.9.3。谢谢! 最佳答案 每当你在计算中得到时髦的数字时使用bigdecimalrequire'bi
如果用户是所有者,我有一个条件来检查说删除和文章。delete_articleifuser.owner?另一种方式是user.owner?&&delete_article选择它有什么好处还是它只是一种写作风格 最佳答案 性能不太可能成为该声明的问题。第一个要好得多-它更容易阅读。您future的自己和其他将开始编写代码的人会为此感谢您。 关于ruby-on-rails-如果条件与&&,是否有任何性能提升,我们在StackOverflow上找到一个类似的问题:
我是Rails的新手,我遇到了一个错误,但我似乎找不到问题所在。这是日志:[32651:ERROR]2012-10-0913:46:52::comparisonofFloatwithFloatfailed[32651:ERROR]2012-10-0913:46:52::/home/sunny/backend/lib/analytics/lifetime.rb:45:in`each'/home/sunny/backend/lib/analytics/lifetime.rb:45:in`max'/home/sunny/backend/lib/analytics/lifetime.rb:45