如何在不添加额外数字的情况下将float转换为double? [英] How to cast float to double without extra digits added?

查看:48
本文介绍了如何在不添加额外数字的情况下将float转换为double?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个有趣的问题:将 float -99.9f 转换为 double 变量时,该变量的值为<代码> -99.9000015258789 因此,此单元测试失败:

I came across an interesting problem: when I convert the float value -99.9f to a double variable, that variable's value is -99.9000015258789 Therefore this unit test fails:

float f = -99.9f;
double d = (double)f;
Assert.AreEqual(-99.9, d);

我知道在各个地方都增加了32位.但是,如果我直接将其赋值为 -99.9000000000000 ,则可以将其很好地表示为双精度,如本单元测试所示:

I understand that an extra 32 bits are being added in various places. Yet, the value I'm after, -99.9000000000000, is represented just fine as a double if I assign it directly, as evidenced by this unit test:

double d2 = -99.9;
Assert.AreEqual(-99.9, d2);
Assert.AreEqual(-99.9000000000000, d2);

所以我的最终问题是:是否可以采用 -99.9f 并将其转换为 double ,使其真正等于 -99.9000000000000 ?

So my ultimate question is: is it possible to take -99.9f and convert it to a double such that it will truly equal -99.9000000000000?

编辑

目前,我发现了一种变通方法,该变通方法似乎对我的特定应用程序有效(即,我不知道提前将浮点数舍入到多少位数的精度):

I found a workaround that, for the moment, seems to work well for my particular application (i.e. one where I don't know ahead of time how many digits of precision to round the float to):

float f = -99.9f;
double d = double.Parse(f.ToString("r"));
Assert.AreEqual(-99.9, d);

我不确定这是否适用于所有情况;欢迎发表评论

I'm not sure if this would work for all cases; comments are welcome

编辑2 根据Lasse V. Karlsen在下面的回答,解决方案是使用等效于AreEqual方法的MSTest:

EDIT 2 As per Lasse V. Karlsen`s answer below, the solution was to use the MSTest equivalent to the AreEqual method:

Assert.AreEqual(-99.9, d, 0.00001);

请注意,如果我要向该增量值再添加一个零,则测试将失败

Note that if I were to add one more zero to that delta value, the test would fail

推荐答案

问题是99.9不能准确地用 Single Double 表示(与C#对应)关键字 float double ).

The problem is that 99.9 cannot be represented exactly in either Single or Double (corresponds to the C# keywords float and double).

这里的问题是.9必须写为2的幂的小数之和.因为基础表示是位,所以我们只能针对每个位位置说它是开和关,并且小数侧的每个位位置都表示1/2 ^ N,这意味着0.9可以这样表示:

The problem here is the .9 which has to be written as a sum of fractions of powers of two. Because the underlying representation is bits, we can only for each bit position say that it is on and off, and each bit position on the fraction side means 1/2^N, which means that 0.9 can be represented like this:

1   1   1    1    1      1      1      1       1       1
- + - + - + -- + --- + ---- + ---- + ----- + ----- + ------ + ....
2   4   8   64   128   1024   2048   16384   32768   262144

最后,无论有多少位,您要么要么最终略低于0.9,要么略高于0.9,因为不能用这样的位精确地表示0.9,但这就是存储浮点数的方式.

In the end you will either end up just slightly below 0.9 or slightly above 0.9, regardless of how many bits you have because 0.9 cannot be exactly represented with bits like this, but this is how floating point numbers are stored.

通过调用 .ToString()或仅通过 Console.WriteLine " + f ,甚至在调试器中进行检查,都可能需要四舍五入,这意味着您将看不到要存储的确切数字,而只能看到四舍五入/格式化返回它的方式.

The on-screen representation you see, either by calling .ToString(), or just Console.WriteLine, or "" + f, or even inspecting in the debugger, are all subject to possibly being rounded, which means you will not see the exact number that is being stored, only how the rounding/formatting returned it.

这就是为什么在比较浮点值时,应该始终使用"epsilon"比较.

This is why, when comparing floating point values, you should always do so using an "epsilon" comparison.

您的单元测试基本上说:只要实际值与期望值完全相同,我们就可以了",但是在处理浮点值时,这种情况很少发生.实际上,我会说这种情况很少发生,以至于当您同时使用常量和期望常量且它们属于同一类型时,除了一件事之外,您不会立即发现它.

Your unit-test basically says "As long as the actual value is exactly the same as the expected value, we're OK", but when dealing with floating point values this will rarely happen. In fact, I would say that it would happen so seldom that you would discover it immediately except for one thing, when you use constants as both expected and actual and they're of the same type.

相反,您应该编写代码来测试实际值是否与期望值足够近,其中足够接近"与可能值的范围相比很小,并且可能每种情况都不同.

Instead you should write your code to test that the actual value is close enough to the expected value, where "close enough" is something very small compared to the range of possible values, and will likely differ for each case.

您不应该期望Single和Double可以代表完全相同的值,因此来回转换甚至可能最终返回不同的值.

You should not expect that Single and Double can represent the exact same values either so casting back and forth may even end up returning different values.

还要注意,在.NET中,由于DEBUG和RELEASE的构建,浮点计算的行为略有不同,因为处理器的内置FPU部分通常可以比存储类型更高的精度运行,这意味着如果编译器/优化器/抖动最终使用FPU进行了一系列计算,其结果与将值临时存储在编译器生成的变量中时的结果可能略有不同.

Also be aware that with .NET, floating point calculations behave slightly different in regards to DEBUG and RELEASE builds since the built-in FPU part of the processor can usually operate with higher precision than the storage types, this means that if the compiler/optimizer/jitter ends up using the FPU for a sequence of calculations, the outcome can be slightly different from when the values are temporarily stored in variables generated by the compiler.

这就是编写比较的方式:

So this is how you would write a comparison:

if (Math.Abs(actual - expected) < 0.000001) { ... }

其中 0.000001 是足够接近".

就许多单元测试框架(例如NUnit)而言,您可以要求比较将其考虑在内,如下所示:

In terms of many unit-test frameworks, like NUnit, you can ask the comparison to take this into account like this:

Assert.AreEqual(-99.9, d2, 0.000001);

此外,如果您想了解更多有关此的内容,本文将介绍很多细节:

Also, if you want to learn more about this, this article has a lot of gory detail:

这篇关于如何在不添加额外数字的情况下将float转换为double?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆