23 条回复  ·  2513 次点击
FalconD 初学 2024-11-28 00:50:29
在这种意义下 C 的行为没有问题 因为 -1/3 + -1%3 == -1 C 只是约定 / 代表 quotation, % 代表 reminder
zzzsy 初学 2024-11-28 00:59:15
除法又不是只有欧几里得除法一种
majula 初学 2024-11-28 01:02:19
编程语言提供的基本数学运算是方便开发者编写程序的,而不是用来进行数学研究的。要想做后者,应当使用专门的软件(比如 Scilab ) 非得要求编程语言中的概念在数学上“正确”,无异于耍流氓 ---- 题外话,有“数学洁癖”的人最常吐槽的是众多编程语言的“变量”,完全跟数学上的“变量”是不同的东西 不过在这一点上 C 语言并没有中枪,因为 C 语言并没有“变量”这一概念(翻一翻标准手册,会发现 variable 这个词唯一出现在的地方是 VLA )
angrylid 小成 2024-11-28 01:05:09
无奖竞猜:有一种主流编程语言 0/0 不会抛出异常且可以得到合法返回值
ZE3kr 小成 2024-11-28 01:05:15
错的多了就成标准了。HTTP 里 referrer 错误拼写成了 referer ,但现在用的都是错误的拼写
secondwtq 小成 2024-11-28 01:07:05
这些语言的行为在它们自己的体系里是自洽的——比如 C 的浮点数转整数会直接把浮点部分切掉,而 C 的除法,商也是把浮点部分切掉,然后根据此算出余数。如果用传统香烟,啊不传统余数,那同时算出的商和余数会不满足 商*除数+余数=被除数 这一基本原则,这个问题显然更严重。 注意这个行为是 C99 之后才有的,之前没有定义,不过 C99 之前标准库里定义了 div() 函数,可以同时算出商和余数,是一直遵循这个行为的。主流实现比如 x86 的 idiv 指令应该一直都是这样。 C 标准库对浮点数还定义了 fmod() 和 remainder() 两个函数,两个采取了不同的定义,remainder() 函数对应的是 IEEE 754 标准定义的 remainder 操作。fmod() 函数我没有在标准里找到对应。 Python 虽然浮点强转整数也是切,但是貌似实际用得不多,默认的 / 不能整除时直接给浮点,// 和 % 也是一致的。 至于拿计算机语言强行追求贴合数学定义我觉得大可不必,光浮点数就很头疼。等下个 IEEE 754 标准更新之后,可能会有很多符合该标准的实现,但是可能大多数人不会用。
coderluan 初学 2024-11-28 01:09:44
作者说只有 python 是对的,其他语言是错的,从数学的角度我并不反对。 但是作者说其他编程语言是因为 C 语言这么做,所以才跟着这么做的。我感觉作者有点太不看不起其他编程语言了吧,那其他语言和 C 语言不一致情况怎么解释,其他语言这会又不怕了吗?这就是明显的拉踩行为啊。
cooltechbs 小成 2024-11-28 01:45:16
谈数学怎么能不提 Fortran ,Fortran 是怎么处理的(我真的不懂,真心发问)? 而其他语言“错”的根源肯定也不是 C ,而是汇编/机器码。这方面 ARM 、MIPS 又是怎么处理的?
vvhy 小成 2024-11-28 01:47:19
两种定义在数学上都是自洽的
secondwtq 小成 2024-11-28 01:50:15
我记得这个问题我很久之前折腾过,不过具体怎样忘了(当时也没搞 Numerics ),我翻了一下记录,有这么一篇论文: dl.acm.org/doi/pdf/10.1145/128861.128862 The Euclidean Definition of the Functions div and mod 刚才搜到了这个 github.com/WebAssembly/design/issues/250 Semantics of signed integer divide and remainder · Issue #250 · WebAssembly/design · GitHub 根据这个 thread ,最早用 truncating division 的是 Fortran ,原因是早期机器上多不使用 2's complement 表示,truncating division 更好实现,C 出于和 Fortran 兼容的考虑,最后也用了 truncating division 。但是现在的 2's complement 表示上,Euclidean division 可能更好实现(见上面论文,另外两个都引用了 Guy Steele 的 Arithmetic Shifting Considered Harmful ,不过这个我还没看)。但是 truncating division 作为前 2's complement 时代的习惯保留下来了。 所以可能还真不是 C 带的头。至于是不是真的 Fortran 先干的我也不确定( Fortran 66 标准里面我没找到,77 里面倒是有,不过那时候已经有原始的 C 了),但是考古只考到 C 大概是不合格的,就算暴论也没上面那个 thread 有活。 另外上面的“好实现”指得是用 ASR 操作来模拟,硬件除法器有自己的算法,我还没看过。
返回顶部