如何将浮点数与 int 正确相乘并获得仅受有效数字影响的结果
How to correctly multiply a float with an int and get a result only influenced by significant digits?
我有代码在浮点数(表示秒(和 int64(代表纳秒(之间转换,从浮点数中获取小数位6
int64_t nanos = f * 1000000000LL;
但是,存储在浮点数中的许多十进制值无法在二进制浮点数中准确表示,因此当我的浮点数14.2f
时,我得到的结果14199999488
.目前我通过计算基数点之后的大量数字来解决此问题
const float logOfSecs = std::log10(f);
int precommaPlaces = 0;
if(logOfSecs > 0) {
precommaPlaces = std::ceil(logOfSecs);
}
int postcommaPlaces = 7 - precommaPlaces;
if(postcommaPlaces < 0) {
postcommaPlaces = 0;
}
然后将浮点数打印成字符串,让Qt正确舍入浮点数。然后,我将字符串解析为逗号前后的整数,并用整数算术将它们乘以。
const QString valueStr = QString::number(f, 'f', postcommaPlaces);
qint64 nanos = 0;
nanos += valueStr.section(".", 0, 0).toLongLong() * 1000000000LL;
if(postcommaPlaces) {
nanos += valueStr.section(".", 1).toLongLong() *
std::pow(10.0, 9 - postcommaPlaces);
}
这工作正常,但我想知道是否有更好、也许更快的方法来做到这一点?
通过将值存储在已经造成损害的float
中,您已经丢失了原始数字。您可以猜测可能已预期的值,然后进行舍入,或者如果您只是尝试为用户显示一个值,则可以舍入到较小的小数位数。
相反,您可以通过在整个代码库中使用定点int64_t
表示来解决所有这些问题,从不与float
相互转换,并避免在每次转换期间丢弃精度。
如果您想四舍五入到小数点后一位
#include <iostream>
int main()
{
float f = 14.2f;
long long n = f * 1000000000LL;
std::cout << "float: " << n << 'n';
n = (f + 0.05) * 10;
n *= 100000000LL;
std::cout << "rounded: " << n << 'n';
return 0;
}
小数点后两位是(f + 0.005) * 100
, ...,小数点后六位
n = ((long long)((f + 0.0000005) * 1000000)) * 1000LL;
如果要考虑有效数字(所有数字(,则必须首先取log10(f)
然后调整小数位的舍入。
但正如@MarkB已经说过的,如果你首先使用int64_t
,你根本不需要这个。
如其他答案所述,舍入到任意数量的十进制数字与打印浮点数密切相关。由于正确舍入的算法相当复杂,因此正确的最简单的方法是使用 printf 本身。
请注意,您不一定必须提供任意数量的位数,另一种方法是使用最短的小数点,该小数点将以 2 为基数不变地转换回来。这样的算法用于在Scheme,Java,Python,Squeak/Pharo等中打印浮点数...不幸的是,libm printf 和任何标准 C 库都不兼容。
方案甚至更好,因为它打印 *,当您施加固定数量的数字时,数字并不重要(* 表示任何数字在以 2 为底转换回时都会导致相同的浮点数(。
在本期 http://code.google.com/p/pharo/issues/detail?id=4957 有一个名为 Float-asMinimalDecimalFraction.st 的附件,其中包含 Smalltalk 中用于打印的算法与 Scheme 类似的实现,但输出分数(两个任意长度整数的比率(而不是 ASCII 字符串。
因此,例如,尽管 14.2f 在内部表示为 14.19999980926513671875,但还为时不晚,您可以检索正确舍入到它的最短小数部分是 (142/10(。
在 Smalltalk 中使用此类代码,您的问题的解决方案很简单:
nanos := (floatingPointSeconds asMinimalDecimalFraction * 1e9) rounded.
但是上面的代码使用了精确的算术(1e9
是一个整数(和任意长度的整数。
请注意,在浮点数中执行乘法会很糟糕:
nanos := (aFloat * 1e9) asMinimalDecimalFraction rounded.
事实上,尽管 1e9 asFloat 转换是精确的,但其有效数跨越 21 位,因此浮点乘法很可能会累积舍入误差并加剧检索短分数的问题。
虽然在技术上以某种方式回答了这个问题,但由于以下原因,我个人认为上述算法在实用上是不合适的:
在没有任意精度算术库帮助的情况下,使用低级 C/C++ 指令执行此操作并不是获得结果的最快途径
它非常有限,因为它不适用于具有多个舍入误差的计算结果(它们在统计上需要许多数字(
如果您可以简单地避免使用 Float 并使用 nanos int,那就太过分了
尽管如此,知道它的存在总是很高兴......
- 最高有效数字侧的第N位
- 无符号的 int 到有效数字
- C++ 检查结果数组中有多少次数字
- 如何在C++中有效地将数字值重新分配给字符数组
- 将 C 函数转换为 C++ 以检查数字是否有效
- 如何检查用户的输入是否有效以及我正在寻找的数字?
- 我们如何并行运行算法的 n 个实例并以有效的方式计算结果函数的平均值?
- 如何知道文本文件中的输入是否是 C++ 中的有效数字
- 将一个数字拆分为多个数字,每个数字只有一个有效位
- 如何以更有效的方式检查一个数字是否是素数?
- 为什么我输入的数字只读到6个有效数字
- 如何删除C++中浮点数的最后一个有效数字/尾数位
- 在C++中将字符串转换为带完整有效数字的浮点值[修复了在开发人员C++中工作的问题]
- 为什么用“std::chrono”进行时差测量时,与“double”类型相比,用“float”类型给出的有效数字更多
- 有效数字在增加
- 如何检查两个数字是否在浮点类型的精度限制的有效数字"x"范围内?
- 在没有科学记数法的c++中格式化有效数字
- 刷新到python浮点数中的零和/或有效数字
- 为什么在显示矢量<长双精度>时看不到所有有效数字?
- 如何将浮点数与 int 正确相乘并获得仅受有效数字影响的结果