提问人:user3414663 提问时间:8/3/2023 更新时间:8/4/2023 访问量:92
IEEE754单精度浮点数的字节表示形式不一致
Inconsistency between byte representations of IEEE754 single-precision floats
问:
当使用 Common Lisp 读取包含单精度浮点数的二进制编码的数据文件时,会出现此问题,这些编码可能是从 C/C++ 编写的。在Lisp中,我使用了ieee-floats包。然而,这导致浮点数的有效数与读取相同浮点数的 C 程序读取的浮点数略有不同。decode-float32
从表面上看,代码似乎忠实于维基百科页面上IEEE754单个浮点数的描述。此外,这个在线 IEEE-754 浮点转换器也符合该行为。ieee-floats:decode-float32
所以两个问题。为什么C版本不一致?你怎么能在Lisp中复制C的行为呢?
这一切都是在 x86_64 GNU/Linux 上使用最新版本的 sbcl、gcc 和 clang 进行的。
下面是我用来试验和确认行为的一些代码。
这里有一些 C 代码来生成一些随机浮点数及其表示为字节
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
int COUNT = 16;
void main () {
float f;
uint32_t i;
char* pf = (char *)&f;
char* pi = (char *)&i;
for(int j=0; j<COUNT; j++) {
f = (float)rand()/(float)(RAND_MAX/2.0) - 1.0;
pf = (char *)&f;
pi = (char *)&i;
memcpy(pi, pf, sizeof(float));
printf("%9f #x%x\n", f, i);
}
}
这里有一些 Lisp 代码来读取这些浮点数和字节,并将 Lisp 编码与 C 编码进行比较。
(ql:quickload '(:iterate :ieee-floats))
(defun scan (&optional (file "floats_and_bytes.dat"))
(with-open-file (in file)
(iter (for f next (read in nil))
(for b next (read in nil))
(while (and f b))
(format t "~9f ~x ~x ~b~%"
f
(ieee-floats:encode-float32 f)
b
(- (ieee-floats:encode-float32 f) b)))))
答:
3赞
chux - Reinstate Monica
8/4/2023
#1
普通浮点
数具有 24 位二进制有效数,最多需要 9 个有效十进制数字来唯一打印值(以及符号和可能的指数)。float
printf("%9f #x%x\n", f, i);
使用 9 个字符打印 (-1.0 ... + 1.0) 范围内的值:或 、 、 、 6 位数字。这充其量是 6 位有效数字。' '
'-'
'0'
'.'
建议的更改(以及一些小的 C 改进):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
int COUNT = 16;
int main() {
union {
float f;
uint32_t i;
} x;
for (int j = 0; j < COUNT; j++) {
x.f = (float) rand() / ((float) (RAND_MAX /2 + 1)) - 1.0f;
printf("% .9g #x%lx\n", x.f, (unsigned long) x.i);
}
}
示例输出
0.380002022 #x3ec28fa0
0.0108368397 #x3c318d00 // 10 digits after the '.', yet 9 significant digits.
0.182981133 #x3e3b5f68
0.109569788 #x3de06620
-0.243142366 #xbe78fa50
-0.484535754 #xbef81512
-0.585235715 #xbf15d202
0.252523899 #x3e814ad0
-0.319745898 #xbea3b5bc
0.687703729 #x3f300d5a
-0.862443924 #xbf5cc920
-0.180186212 #xbe3882bc
0.75998807 #x3f428e94
-0.3610394 #xbeb8da28
0.961136341 #x3f760d08
-0.829990387 #xbf547a40
评论
rand
printf("%.9g #x%x\n", f, i);
%f
%9f
%99f
%99f
”-- 它们指定字段宽度,但不指定精度。的默认精度为小数点后 6 位。这是 C 标准规定的,也就是说,它不是实现细节。如果您的实现默认打印 7 位小数,则您的实现不符合标准。若要指定精度,必须使用后跟精度,例如 .
%f
.
%10.7f
%.7f