为啥 F# 中的幂运算符仅适用于浮点数?

Posted

技术标签:

【中文标题】为啥 F# 中的幂运算符仅适用于浮点数?【英文标题】:Why does the power operator in F# only work for floating point numbers?为什么 F# 中的幂运算符仅适用于浮点数? 【发布时间】:2011-07-17 01:57:03 【问题描述】:

我从未见过一种语言只有指数或幂运算符只取浮点数?

例如:

2 ** 2 抛出错误The type 'int' does not support any operators named 'Pow'

这个设计决定有正当理由吗?

【问题讨论】:

+1,正在寻找一些很好的答案:) 我同意唯一的版本是 nonint ** nonint 似乎有点奇怪 还有哪些其他语言为整数实现了**?所有我能想到的都只是强制浮动。 @Gabe:在 Python 中,“10100”计算为长整数。它不会强制浮动。 “10.**100”和“10100”。被评估为浮点数。 【参考方案1】:

简短的回答是因为它对整数类型不是很有用,即使是 int64s。 2^26 只给你〜1.84467441E19。因此,如果您有两个值 X 和 Y 都大于 19,那么幂运算符将导致溢出。

我同意它对小值很有用,但它通常不适用于整数类型。

【讨论】:

-1,F# 具有不易溢出的任意精度整数。 刚刚在 F# 交互中尝试过:> 2.0**26.0 ;; val it : float = 67108864.0 @Gabe - ** 是“重载”,只是不在 int 和 int64 上,请参阅@kvb 的答案。它适用于像 3I ** 1000 这样的大整数。【参考方案2】:

F# 所基于的语言是 OCaml,它不进行运算符重载或自动数据强制(他们更喜欢显式强制)。

因此,即使添加双精度数也需要不同的运算符 (+.)。我不确定这是否是 F# 对其严格要求的地方,但我猜是。

在 Python 或 Scheme 等动态语言中,如果数字太大,您会自动将数据强制转换为更大的数据存储。例如,您可以使用具有整数指数的整数来为结果提供大整数。

OCaml 和 F# 具有极端类型安全的精神。

【讨论】:

C 的pow() 需要double 并产生double,并且仅由于自动强制而适用于其他类型。 (fpow() 使用 float 代替。)OCaml 和 F# 不会自动强制,所以你会被浮点数困住。也许如果有人用过它,就会有一个整数版本。 F# 支持重载运算符(实际上(**) 是重载的,只是不支持ints 作为参数)。 是的,对不起,我不是说 F# 不支持重载运算符,我的意思是 OCaml 不支持。我可以看出它是多么令人困惑。【参考方案3】:

对于积分幂,F# 提供了另一个运算符:pown。另外,作为旁注,(**)pown 都被重载,因此完全可以将它们与提供适当成员的其他类型一起使用((**) 的静态 Pow 方法;(*)(/) 运算符和静态One 属性(在pown 的情况下)。

我无法解释为什么 F# 团队选择不在 int 上模拟 Pow 成员,但也许他们认为这并不紧迫,因为可以使用 pown 运算符来代替(并且因为在大操作数的情况下,首先转换为浮点数可能更有意义。

【讨论】:

确实,** 有签名^a -> ^b -> ^a when ^a : (static member Pow : ^a * ^b -> ^a),所以3I ** 10003.0f ** 3.2f3.0 ** 3.2 都是有效的。【参考方案4】:

(**)pown 是两个不同的东西。当你看到(**)时,你可以想到使用对数的数学公式。当您看到pown 时,它只是一系列乘法运算。我知道一开始可能会令人惊讶/困惑,因为大多数其他语言都没有这样的区别(主要是因为整数经常被隐式转换为浮点值)。即使在数学上,也有一点区别:参见Wikipedia entry,第一个定义仅适用于正整数指数。

由于它们是两个不同(但相关)的东西,它们有不同的签名。这里是(**)

^a -> ( ^b ->  ^a) when  ^a : (static member Pow :  ^a *  ^b ->  ^a)

这里是pown:

^a -> (int ->  ^a)
when  ^a : (static member get_One : ->  ^a) and
      ^a : (static member ( * ) :  ^a *  ^a ->  ^a) and
      ^a : (static member ( / ) :  ^a *  ^a ->  ^a)

如果您创建自己的类型,您只需要拥有One(*)(/) 即可使其与pown 一起使用。该库将为您执行循环(已优化,不是天真的 O(n))。

如果您想在您的类型上使用 (**) 运算符来处理非整数值,则必须编写完整的逻辑(并且它与 pown 中的算法不同)。

我认为将这两个概念分开是一个很好的设计决策。

【讨论】:

很好的答案,我完全同意,除了 bigint 实现 Pow 似乎是一个不一致的选择。也许可能的 bigint 特定优化非常引人注目。 文档说“为两个大整数返回 n^m”,但签名是“BigInteger * int32 -> BigInteger”。我认为某处有些混乱。 实际上,F# 库中的 BigInt 实现了 Pow 以使二进制类型与 .NET 4.0 中的 System.Numerics.BigInteger 类兼容。 我同意:(**) 是对权力的更数学定义。此外,您不能使用签名为 int -> int -> int 的默认 pow 运算符,因为 2 ^ -2 = 0.25。默认情况下,幂运算必须返回一个浮点数。

以上是关于为啥 F# 中的幂运算符仅适用于浮点数?的主要内容,如果未能解决你的问题,请参考以下文章

MPI 派生数据类型适用于浮点数,但不适用于双精度数。是对齐问题吗?

支付接口的幂等性设计

分布式系统中的幂等性问题

有人能解释一下为啥“运算符优先级”适用于 javaScript 中的“||”、“&&”等逻辑运算符吗

为啥 f:validateDoubleRange 仅适用于 @SessionScoped?

Python 中的幂运算符 (**) 翻译成啥?