将String转换为Int来检查溢出 [英] Convert String to Int checking for overflow

查看:114
本文介绍了将String转换为Int来检查溢出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我试图将一个很长的整数转换为 Int 时,我惊讶地发现没有错误:

 前奏>阅读123456789012345678901234567890:: Int 
-4362896299872285998

readMaybe from Text.Read 模块给出相同的结果。



两个问题:




  • 我应该调用哪个函数来执行安全转换?

  • 地球上最安全的语言允许这些不安全的事情吗?



更新1:

这是我尝试编写检查边界的 read 版本:

  { - #LANGUAGE ScopedTypeVariables# - } 

parseIntegral :: forall a。 (积分a,有界a)=>字符串 - >也许
parseIntegral s = integerToIntegral(读取s :: Integer)其中
integerToIntegral n | n< fromIntegral(minBound :: a)= Nothing
integerToIntegral n | n> fromIntegral(maxBound :: a)= Nothing
integerToIntegral n = Just $ fromInteger n

是它是我能做的最好的吗?

解决方案

背景:为什么unchecked overflow实际上很棒



Haskell 98明确地没有指定溢出行为,这对于实现者很好,对其他人不利。 Haskell 2010在两节中讨论了它 - 在从Haskell 98继承的部分中,它明确地没有指定,而在 Data.Int 数据部分.Word ,它被指定。这种不一致将最终得到解决。



GHC非常友好地明确指出:


所有的算术运算均以2 ^ n为模进行,其中n是类型中的位数。

这是一个非常有用的规格。特别是,它保证 Int Word Int64 Word32 等等,形成戒指,甚至主理想环,在加法和乘法之下。这意味着算术将始终正确地工作 - 您可以用许多不同的方式转换表达式和方程式,而不会破坏事物。在溢出时抛出异常会破坏所有这些属性,使编写和推理程序变得更加困难。你唯一需要小心的是当你使用比较运算符,如< 比较 - 固定宽度整数不要形成有序的群体,所以这些操作者有点棘手。



为什么不检查读取有意义



<读整数涉及许多乘法和加法。它也需要快速。检查以确保读取有效并不是那么容易。特别是,虽然很容易找出加法是否溢出,但很难找出乘法是否存在。对于 Int 执行检查读取的唯一明智方式是


  1. 读为整数,检查并转换。 整数算术比 Int 算术要昂贵得多。对于小的东西,比如 Int16 ,可以用 Int 来完成读取,检查 Int16 溢出,然后缩小。

  2. 将十进制数字 maxBound 进行比较c $ c>(或者,对于负数, minBound )。这看起来更有可能是相当有效的,但仍然会有一些成本。正如这个答案的第一部分所解释的那样,没有什么本质上与溢出错误,所以不清楚抛出错误实际上比给出2 ^ n模的回答更好。

    li>


When I tried to convert a very long integer to Int, I was surprised that no error was thrown:

Prelude> read "123456789012345678901234567890" :: Int
-4362896299872285998

readMaybe from Text.Read module gives the same result.

Two questions:

  • Which function should I call to perform a safe conversion?
  • How can the most type safe language on Earth allow such unsafe things?

Update 1:

This is my attempt to write a version of read that checks bounds:

{-# LANGUAGE ScopedTypeVariables #-}

parseIntegral :: forall a . (Integral a, Bounded a) => String -> Maybe a
parseIntegral s = integerToIntegral (read s :: Integer) where
  integerToIntegral n | n < fromIntegral (minBound :: a) = Nothing
  integerToIntegral n | n > fromIntegral (maxBound :: a) = Nothing
  integerToIntegral n = Just $ fromInteger n

Is it the best I can do?

解决方案

Background: why unchecked overflow is actually wonderful

Haskell 98 leaves overflow behavior explicitly unspecified, which is good for implementers and bad for everyone else. Haskell 2010 discusses it in two sections—in the section inherited from Haskell 98, it's left explicitly unspecified, whereas in the sections on Data.Int and Data.Word, it is specified. This inconsistency will hopefully be resolved eventually.

GHC is kind enough to specify it explicitly:

All arithmetic is performed modulo 2^n, where n is the number of bits in the type.

This is an extremely useful specification. In particular, it guarantees that Int, Word, Int64, Word32, etc., form rings, and even principal ideal rings, under addition and multiplication. This means that arithmetic will always work right—you can transform expressions and equations in lots of different ways without breaking things. Throwing exceptions on overflow would break all these properties, making it much more difficult to write and reason about programs. The only times you really need to be careful are when you use comparison operators like < and compare—fixed width integers do not form ordered groups, so these operators are a bit touchy.

Why it makes sense not to check reads

Reading an integer involves many multiplications and additions. It also needs to be fast. Checking to make sure the read is "valid" is not so easy to do quickly. In particular, while it's easy to find out whether an addition has overflowed, it is not easy to find out whether a multiplication has. The only sensible ways I can think of to perform a checked read for Int are

  1. Read as an Integer, check, then convert. Integer arithmetic is significantly more expensive than Int arithmetic. For smaller things, like Int16, the read can be done with Int, checking for Int16 overflow, then narrowed. This is cheaper, but still not free.

  2. Compare the number in decimal to maxBound (or, for a negative number, minBound) while reading. This seems more likely to be reasonably efficient, but there will still be some cost. As the first section of this answer explains, there is nothing inherently wrong with overflow, so it's not clear that throwing an error is actually better than giving an answer modulo 2^n.

这篇关于将String转换为Int来检查溢出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆