1

我正在尝试将两个floats 相乘,如下所示:

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

我想看到的结果是 110895.582,但是当我运行代码时它只给我 110896。大多数时候我都遇到这个问题。任何计算器都能为我提供所有小数的确切结果。我怎样才能达到这个结果?

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

4

4 回答 4

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 个十进制数字的精度,则必须使用doubleand not float。(注意我说的是“十进制数字的价值”,因为你没有得到十进制数字,你得到的是二进制。)就目前而言,1/2 ULP 的错误(完美舍入结果的最坏情况界限)约为0.004。

  2. 如果您想要精确四舍五入的十进制数,则必须使用专门的十进制库来完成此类任务。对于科学家来说, Adouble具有足够的精度,但如果你用钱工作,一切都必须 100% 准确。没有浮点数的钱。

与整数不同,浮点数需要一些实际的工作才能习惯它们的陷阱。请参阅“ What Every Computer Scientist Should Know About Floating-Point Arithmetic ”,这是对该主题的经典介绍。

编辑:实际上,我不确定代码是否会循环三遍。它可能会四舍五入五次,因为aand的常量在b存储时可能先四舍五入为双精度,然后再四舍五入为单精度。但是我对C这部分的规则不是很了解。

于 2012-07-08T09:15:56.533 回答
3

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

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

number2 ≠ 345.34 也是如此。

因此,您从不精确的值开始。

然后乘积将被四舍五入,因为乘法使有效位数增加一倍,但float如果乘以浮点数,则必须再次存储乘积。

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

32112 * 34534 = 1108955808。

这表示 321.12 * 345.34 = 110895.5808。

于 2012-07-08T09:25:33.150 回答
1

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

例如:

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

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

输出:

110895.57812500000000000000

但是,请注意,float它只给出了六位数的精度。为了更好的精度使用double

于 2012-07-08T09:15:43.537 回答
1
  • 浮点变量只是近似表示,而不是精确表示。并非每个数字都可以“适合”浮点变量。例如,无法将 1/10 (0.1) 放入二进制变量中,就像无法将 1/3 放入十进制 1 一样(只能用无穷大的 0.33333 来近似)
  • 输出此类变量时,通常会应用许多舍入选项。除非您全部设置,否则您永远无法确定应用了哪些。对于 << 运算符尤其如此,因为可以告诉流如何在 << 之前进行舍入。

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 需要重新发明所有运算:加法、减法、乘法和除法。或者只是寻找一个十进制库。

于 2012-07-08T09:20:08.257 回答