为啥 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# 支持重载运算符(实际上(**)
是重载的,只是不支持int
s 作为参数)。
是的,对不起,我不是说 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 ** 1000
、3.0f ** 3.2f
、3.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 中的“||”、“&&”等逻辑运算符吗