乘以两个浮点数并不能得到确切的结果

时间:2012-07-08 09:00:47

标签: c floating-point

我试图将两个float乘以如下:

float number1 = 321.12;
float number2 = 345.34;
float rexsult = number1 * number2;

我想看到的结果是110895.582,但是当我运行代码时它只给了我110896.大多数时候我遇到了这个问题。任何计算器都会给出包含所有小数的精确结果。我怎样才能获得这个结果?

编辑:这是C代码。我正在使用XCode iOS模拟器。

4 个答案:

答案 0 :(得分:11)

有很多四舍五入。

float a = 321.12; // this number will be rounded
float b = 345.34; // this number will also be rounded
float r = a * b;  // and this number will be rounded too
printf("%.15f\n", r);

在三次单独的舍入后,我得到110895.578125000000000。

  1. 如果您希望精确度超过6位小数,则 使用double而不是float。 (注意,我说“十进制数字值”,因为你没有得到十进制数字,你得到二进制数。)就目前而言,错误的1/2 ULP(一个完美的最坏情况舍入结果)约为0.004。

  2. 如果您想要精确舍入的十进制数,则必须使用专门的十进制库来执行此类任务。 double对于科学家而言具有足够的精确度,但如果你使用金钱,一切都必须100%准确。没钱浮点数。

  3. 与整数不同,浮点数在您习惯于陷入陷阱之前需要一些实际的工作。请参阅“What Every Computer Scientist Should Know About Floating-Point Arithmetic”,这是该主题的经典介绍。

    编辑:实际上,我不确定代码会轮次三次。它可能会绕五次次,因为ab的常量可能首先舍入为双精度,然后在存储时再舍入为单精度。但我不太清楚这部分C的规则。

答案 1 :(得分:3)

你永远不会得到那样的确切结果。

首先,number1≠321.12,因为该值无法在base-2系统中准确表示。你需要无限数量的位。

同样适用于number2≠345.34。

因此,您首先要使用不准确的值。

然后产品将被舍入,因为乘法会使有效位数加倍,但如果乘以浮点数,则产品必须再次存储在float中。

您可能希望为您的号码使用基于10的系统。或者,如果您的数字只有小数的2位十进制数,您可以使用整数(在这种情况下32位整数就足够了,但最终可能需要64位):

32112 * 34534 = 1108955808。

代表321.12 * 345.34 = 110895.5808。

答案 2 :(得分:1)

由于使用C,您可以使用“%。xf”轻松设置精度,其中x是所需的精度。

例如:

float n1 = 321.12;
float n2 = 345.34;
float result = n1 * n2;

printf("%.20f", result);

输出:

110895.57812500000000000000

但请注意,float仅提供六位精度。为了更好地精确使用double

答案 3 :(得分:1)

  • 浮点变量只是近似表示,而不是精确表示。并非每个数字都可以“适合”浮点变量。例如,没有办法将1/10(0.1)放入二进制变量,就像不可能将1/3放入十进制变量(你只能用无穷无尽的0.33333近似它)
  • 在输出这些变量时,通常应用许多舍入选项。除非你全部设置,否则你永远不能确定它们中的哪一个被应用。对于<<<<<<运营商,因为流可以被告知如何围绕BEFORE<<。

Printf也进行了一些舍入。考虑http://codepad.org/LLweoeHp

float t = 0.1f;
printf("result: %f\n", t);
--
result: 0.100000

嗯,看起来很好。为什么?因为printf默认了一些精度并将输出四舍五入。让我们拨打小数点后的50个位置:http://codepad.org/frUPOvcI

float t = 0.1f;
printf("result: %.50f\n", t);
--
result: 0.10000000149011611938476562500000000000000000000000

那是不同的,不是吗?在625之后,浮动耗尽了容纳更多数据的能力,这就是我们看到零的原因。 double可以容纳更多数字,但二进制中的0.1不是有限的。 Double必须放弃,最终:http://codepad.org/RAd7Yu2r

double t = 0.1;
printf("result: %.70f\n", t);
--
result: 0.1000000000000000055511151231257827021181583404541015625000000000000000

在您的示例中,单独使用321.12就足以造成麻烦:http://codepad.org/cgw3vUKn

float t = 321.12f;
printf("and the result is: %.50f\n", t);
result: 321.11999511718750000000000000000000000000000000000000

这就是为什么在将浮点值呈现给人类之前必须对其进行舍入。

计算器程序根本不使用浮点数或双精度数。它们实现十进制数格式。例如:

struct decimal
{
     int mantissa; //meaningfull digits
     int exponent; //number of decimal zeroes
};

Ofc需要重新发明所有操作:加法,减法,乘法和除法。或者只是寻找一个十进制库。