【问题标题】:Save float result number to third digit, no rounding in C将浮点结果数保存到第三位,C 中不四舍五入
【发布时间】:2017-07-12 17:18:51
【问题描述】:

如何将结果四舍五入到第三位后的第三位。

float result = cos(number);

请注意,我想将结果保存到第三位,不四舍五入。不,我不想用 .3f 打印它,我需要将它保存为新值;

例子:

 0.00367 -> 0.003

注意3 之后不需要额外的零。

另外,我需要能够获得第三位数字。例如,如果它是 0.0037212,我想得到 3 并在某些计算中将其用作 int。

【问题讨论】:

  • 详细信息:由于typical float 不能代表0.009 之类的数字,float x = 0.009; 将导致x 的精确值为0.0089999996125698089599609375。在这种情况下,float 的第 3 位是 8 而不是 9。这样可以吗?
  • 回复,“我不想打印它。”这将是一个问题,因为float 值没有十进制数字。十进制数字仅出现在 float 值的打印表示中。没有完全等于 0.003 的 float 值。

标签: c floating-point rounding math.h trigonometry


【解决方案1】:

0.00367 -> 0.003

float 通常可以精确地表示大约 232 个不同的值。 0.00367 和 0.003 不在该集合中。

float 与 0.00367 最接近的是 0.0036700000055134296417236328125
float 与 0.003__ 最接近的是 0.0030000000260770320892333984375

我想将结果保存到第三位

这个目标需要妥协。将结果保存到接近 0.001 倍数的 float。

按 1000.0 缩放、截断和除以 1000.0 将适用于大多数值。

float y1 = truncf(x * 1000.0f) / 1000.0f;

上面给出了一个稍微错误的答案,其中一些值接近x.xxx000... 和x.xxx999...。使用更高的精度可以解决这个问题。

float y2 = (float) (trunc(x * 1000.0) / 1000.0);

我想获得 3 并将其用作 int 进行一些计算。

跳过未缩放部分,只保留一位fmod()。

int digit = (int) fmod((trunc(x * 1000.0), 10);
digit = abs(digit);

最后,我怀疑这种方法不会完全满足 OP 未说明的“在某些计算中将其用作int”。 FP 数学有很多字幕,尤其是在尝试使用 binary FP 时,就像大多数 double 一样,以某种 decimal 方式。

也许以下将满足 OP 的目标,即使它做了 一些 舍入。:

int third_digit = (int) lround(cos(number)*1000.0) % 10;
third_digit = abs(third_digit);

【讨论】:

  • 谢谢,最后的代码要点是我所需要的,但一切都得到了赞赏。
【解决方案2】:

您可以将值放大,使用trunc 将其截断为零,然后缩小:

float result = trunc(cos(number) * 1000) / 1000;

请注意,由于浮点数的不精确性,结果不会是精确值。

如果您要专门提取第三个十进制数字,您可以按如下方式进行:

int digit = (int)(result * 1000) % 10;

这将放大数字,使相关数字位于小数点左侧,然后提取该数字。

【讨论】:

  • 我不希望数字四舍五入,数字必须保持不变。例如,如果我执行 0.0036,则结果变为 0.004。我希望它是 0.003。
  • floor 函数会这样做,至少对于正值。我已经更新为使用 trunc 代替它适用于正值和负值。
  • @KarinaK 您需要使用%.3f 格式说明符才能使其看起来如此。值 0.003 不能精确地表示为二进制浮点数。它实际上可能是 0.003000001 或 0.002999999。
  • @KarinaK 作为浮点数,不完全是。也许您应该更新您的问题,即您真正试图通过这样做来完成什么。
  • @KarinaK,关于“我不希望数字被四舍五入”听起来您正在尝试将 float 值转换为 int。这种转换的艺术术语总是“四舍五入”。不同种类的舍入指定为“向上舍入”、“向下舍入”、“向零舍入”、“向最接近的舍入”……但它始终称为“舍入”。
【解决方案3】:

你可以从除以0.001的余数中减去:

result -= fmod(result, 0.001);

Demo

更新:

问题已更新,要求非常矛盾。如果您有一个精确 0.003 数字,那么它后面会有无限 个零,这是数字的数学属性。 OTOH,float 表示不能保证每个精确的 3 位十进制数字都会被准确表示。要解决这个问题,您需要放弃使用float 类型并改用某种定点表示。

【讨论】:

  • 鉴于0.001 不能完全用二进制浮点表示,如果这总是给出正确的结果,我会感到惊讶。我会要求一些论据。
  • @EOF 你是对的,我不想进入这个,因为 OP 要求将结果放在float 中,这使得整个要求具有“截断”值在这有点可疑。我建议完全使用定点表示。
  • 看问题,我更新了。它提供了足够好的结果,但我希望将数字保存为 0.00digit 而不是 0.00digit00。
  • @EOF 好点,但我希望“错误”的结果并不常见,因为 result 是 float 并且这个答案使用 double 数学,可以处理许多边缘情况。 OP 的目标仍然只是部分明确。
【解决方案4】:

矫枉过正,使用 sprintf()

double /* or float */ val = 0.00385475337;
if (val < 0) exit(EXIT_FAILURE);
if (val >= 1) exit(EXIT_FAILURE);
char tmp[55];
sprintf(tmp, "%.50f", val);
int third_digit = tmp[4] - '0';

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-17
    相关资源
    最近更新 更多