Module refinery.lib.inno.delphi.runtime
The state that Delphi's run-time library keeps for a process, and what Delphi's compiled code does on the platform that it was built for. A 32-bit Setup computes floats on the x87 under the control word that a script can change. A 64-bit Setup computes with SSE; it keeps the control word that a script sets, but nothing computes under it.
Expand source code Browse git
"""
The state that Delphi's run-time library keeps for a process, and what Delphi's compiled code does
on the platform that it was built for. A 32-bit Setup computes floats on the x87 under the control
word that a script can change. A 64-bit Setup computes with SSE; it keeps the control word that a
script sets, but nothing computes under it.
"""
from __future__ import annotations
import abc
import enum
from fractions import Fraction
from refinery.lib.inno.delphi.errors import EDivByZero, EIntOverflow, EZeroDivide, Undefined
from refinery.lib.inno.delphi.numbers import EXPONENT_LIMIT, FormatSettings, Library
from refinery.lib.inno.delphi.x87 import (
FPU,
MASKED_CONTROL_WORD,
SSE,
WIN64_CONTROL_WORD,
Format,
Real,
)
class Platform(enum.Enum):
"""
The platforms that Delphi compiles Inno Setup for.
"""
Win32 = 32
Win64 = 64
class Operation(enum.Enum):
"""
The arithmetic of Delphi's compiled code.
"""
Add = '+'
Subtract = '-'
Multiply = '*'
Divide = '/'
def units(value: Fraction) -> int:
"""
A Currency as its integer number of units of 1/10000.
"""
return value.numerator * (10000 // value.denominator)
def truncated_division(a: int, b: int) -> tuple[int, int]:
"""
Divides as Delphi's `div` and `mod` do: The quotient is truncated toward zero, and the remainder
has the sign of the dividend.
"""
if b == 0:
raise EDivByZero
quotient = abs(a) // abs(b)
if (a < 0) != (b < 0):
quotient = -quotient
return quotient, a - quotient * b
def _signed64(value: int) -> int:
value &= 0xFFFFFFFFFFFFFFFF
return value - (1 << 64) if value >> 63 else value
_INT64 = range(-0x8000000000000000, 0x8000000000000000)
class Runtime(abc.ABC):
"""
The regional settings that numbers are formatted and parsed with, the control word that a
script reads and writes, the unit that compiled code computes floats with (`fpu`), and the
routines that convert floats to text and back (`library`). The methods answer how Delphi's
compiled code computes where the platforms differ.
"""
platform: Platform
pointer_size: int
variant_size: int
extended_size: int
parses_long_digits_as_infinity: bool
"""
Whether Windows reads a number without exponent beyond the range of a Double from a string as
an infinity instead of failing to convert it.
"""
fpu: FPU
library: Library
def __init__(self, settings: FormatSettings, control: FPU):
self.settings = settings
self._control = control
@property
def control_word(self) -> int:
"""
The control word that `Get8087CW` reads and `Set8087CW` writes.
"""
return self._control.control
@control_word.setter
def control_word(self, value: int):
self._control.control = value
def real_arithmetic(self, op: Operation, a: Real, b: Real, fmt: Format = Format.Extended) -> Real:
"""
Computes with two floats and stores the result into a variable of the given format.
"""
fpu = self.fpu
if op is Operation.Add:
result = fpu.add(a, b)
elif op is Operation.Subtract:
result = fpu.subtract(a, b)
elif op is Operation.Multiply:
result = fpu.multiply(a, b)
else:
result = fpu.divide(a, b)
return fpu.store(result, fmt)
def currency(self, value: Real) -> Fraction:
"""
Converts a float to a Currency: the value times 10000, rounded as a result, and then to an
integer as the unit rounds. On the x87, a value out of range signals an invalid operation
and gives the smallest Currency.
"""
fpu = self.fpu
return Fraction(fpu.integer(fpu.multiply(value, 10000.0)), 10000)
def currency_to_real(self, value: Fraction) -> Real:
"""
Converts a Currency to a float: its units of 1/10000 divided by 10000.
"""
return self.fpu.divide(self.fpu.load(units(value)), 10000.0)
def currency_arithmetic(self, op: Operation, a: Fraction, b: Fraction | int | Real) -> Fraction:
"""
Computes with a Currency and a Currency, an integer, or a float, and stores the result as a
Currency. The unit loads the units of 1/10000 of the Currency, computes with the other
operand, and stores the result as an integer, rounded as the unit rounds. A Currency operand
counts in units, an integer in whole numbers, a float as its value.
"""
fpu = self.fpu
left = fpu.load(units(a))
if isinstance(b, Fraction):
right = fpu.load(units(b))
if op is Operation.Multiply:
result = fpu.divide(fpu.multiply(left, right), 10000.0)
elif op is Operation.Divide:
result = fpu.multiply(fpu.divide(left, right), 10000.0)
else:
result = self.real_arithmetic(op, left, right)
elif op is Operation.Add or op is Operation.Subtract:
right = fpu.load(b * 10000) if isinstance(b, int) else fpu.multiply(b, 10000.0)
result = self.real_arithmetic(op, left, right)
else:
result = self.real_arithmetic(op, left, fpu.load(b) if isinstance(b, int) else b)
return Fraction(fpu.integer(result), 10000)
@abc.abstractmethod
def currency_of_integer(self, value: int) -> Fraction:
"""
Converts an integer to a Currency.
"""
@abc.abstractmethod
def variant_currency(self, value: Real) -> Fraction:
"""
Converts the float of a Variant to a Currency; the caller checks the range.
"""
@abc.abstractmethod
def real_divided_by_currency(self, a: Real, b: Fraction, fmt: Format) -> Real:
"""
Divides a float by a Currency and stores the result into a variable of the given format.
"""
@abc.abstractmethod
def check_currency_divisor(self, b: Fraction):
"""
Raises the error of a division by a Currency of zero, if the platform raises one.
"""
@abc.abstractmethod
def int64_division(self, a: int, b: int) -> tuple[int, int]:
"""
The quotient and the remainder of Int64 operands, truncated toward zero.
"""
class Win32(Runtime):
"""
Delphi's 32-bit code: the x87 computes under the control word of the script, and the library
computes on the same x87.
"""
platform = Platform.Win32
pointer_size = 4
variant_size = 16
extended_size = 10
parses_long_digits_as_infinity = False
def __init__(self, settings: FormatSettings, control_word: int):
fpu = FPU(control_word)
super().__init__(settings, fpu)
self.fpu = fpu
self.library = Library(
unit=fpu,
digits=18,
table_end=15,
exponent_limit=EXPONENT_LIMIT,
minimum_power_is_infinite=False,
)
def currency_of_integer(self, value: int) -> Fraction:
"""
The x87 loads the integer and converts it as a float: a value out of range signals an
invalid operation and gives the smallest Currency.
"""
return self.currency(self.fpu.load(value))
def variant_currency(self, value: Real) -> Fraction:
return self.currency(value)
def real_divided_by_currency(self, a: Real, b: Fraction, fmt: Format) -> Real:
"""
The x87 multiplies the float by 10000 and divides by the units of the Currency.
"""
fpu = self.fpu
return fpu.store(fpu.divide(fpu.multiply(a, 10000.0), fpu.load(units(b))), fmt)
def check_currency_divisor(self, b: Fraction):
return
def int64_division(self, a: int, b: int) -> tuple[int, int]:
"""
The run-time library divides; the smallest Int64 divided by -1 gives itself.
"""
return truncated_division(a, b)
class Win64(Runtime):
"""
Delphi's 64-bit code: SSE computes every float as a Double. The library computes as an x87
with the control word of 64-bit code, whatever the script sets; its table of powers of ten ends
at `10 ** 256` and its `Val` has no limit on the exponent.
"""
platform = Platform.Win64
pointer_size = 8
variant_size = 24
extended_size = 8
parses_long_digits_as_infinity = True
def __init__(self, settings: FormatSettings):
super().__init__(settings, FPU(WIN64_CONTROL_WORD))
self.fpu = SSE()
self.library = Library(
unit=FPU(WIN64_CONTROL_WORD),
digits=17,
table_end=8,
exponent_limit=None,
minimum_power_is_infinite=True,
)
def currency_of_integer(self, value: int) -> Fraction:
"""
64-bit code multiplies the integer by 10000 as a 64-bit integer, which wraps around.
"""
return Fraction(_signed64(value * 10000), 10000)
def variant_currency(self, value: Real) -> Fraction:
"""
Windows' 64-bit conversion rounds the value times 10000 as an x87 with every exception
masked does: at 64 bits of mantissa, then to the nearest integer.
"""
fpu = _NEAREST
return Fraction(fpu.integer(fpu.multiply(value, 10000.0)), 10000)
def currency_arithmetic(self, op: Operation, a: Fraction, b: Fraction | int | Real) -> Fraction:
"""
Two Currency values, and a Currency and an integer that are added or subtracted, compute in
units as 64-bit integers: sums wrap around, a product out of range raises an overflow, and
a quotient by zero a division by zero. Products and quotients round half to even. What a
quotient out of range gives is not known.
"""
if not isinstance(b, Fraction) and not (isinstance(b, int) and op in (Operation.Add, Operation.Subtract)):
return super().currency_arithmetic(op, a, b)
left = units(a)
right = units(b) if isinstance(b, Fraction) else b * 10000
if op is Operation.Add:
return Fraction(_signed64(left + right), 10000)
if op is Operation.Subtract:
return Fraction(_signed64(left - right), 10000)
if op is Operation.Multiply:
result = round(Fraction(left * right, 10000))
if result not in _INT64:
raise EIntOverflow
return Fraction(result, 10000)
if right == 0:
raise EZeroDivide
result = round(Fraction(left * 10000, right))
if result not in _INT64:
raise Undefined('64-bit Delphi divides two Currency values whose quotient is out of range in a way that is not modelled.')
return Fraction(result, 10000)
def real_divided_by_currency(self, a: Real, b: Fraction, fmt: Format) -> Real:
"""
64-bit code divides the float by the units of the Currency and multiplies by 10000.
"""
fpu = self.fpu
return fpu.store(fpu.multiply(fpu.divide(a, fpu.load(units(b))), 10000.0), fmt)
def check_currency_divisor(self, b: Fraction):
if b == 0:
raise EZeroDivide
def int64_division(self, a: int, b: int) -> tuple[int, int]:
"""
The processor divides; the smallest Int64 divided by -1 overflows.
"""
if a == -0x8000000000000000 and b == -1:
raise EIntOverflow
return truncated_division(a, b)
_NEAREST = FPU(MASKED_CONTROL_WORD)
Functions
def units(value)-
A Currency as its integer number of units of 1/10000.
Expand source code Browse git
def units(value: Fraction) -> int: """ A Currency as its integer number of units of 1/10000. """ return value.numerator * (10000 // value.denominator) def truncated_division(a, b)-
Divides as Delphi's
divandmoddo: The quotient is truncated toward zero, and the remainder has the sign of the dividend.Expand source code Browse git
def truncated_division(a: int, b: int) -> tuple[int, int]: """ Divides as Delphi's `div` and `mod` do: The quotient is truncated toward zero, and the remainder has the sign of the dividend. """ if b == 0: raise EDivByZero quotient = abs(a) // abs(b) if (a < 0) != (b < 0): quotient = -quotient return quotient, a - quotient * b
Classes
class Platform (*args, **kwds)-
The platforms that Delphi compiles Inno Setup for.
Expand source code Browse git
class Platform(enum.Enum): """ The platforms that Delphi compiles Inno Setup for. """ Win32 = 32 Win64 = 64Ancestors
- enum.Enum
Class variables
var Win32-
The type of the None singleton.
var Win64-
The type of the None singleton.
class Operation (*args, **kwds)-
The arithmetic of Delphi's compiled code.
Expand source code Browse git
class Operation(enum.Enum): """ The arithmetic of Delphi's compiled code. """ Add = '+' Subtract = '-' Multiply = '*' Divide = '/'Ancestors
- enum.Enum
Class variables
var Add-
The type of the None singleton.
var Subtract-
The type of the None singleton.
var Multiply-
The type of the None singleton.
var Divide-
The type of the None singleton.
class Runtime (settings, control)-
The regional settings that numbers are formatted and parsed with, the control word that a script reads and writes, the unit that compiled code computes floats with (
fpu), and the routines that convert floats to text and back (library). The methods answer how Delphi's compiled code computes where the platforms differ.Expand source code Browse git
class Runtime(abc.ABC): """ The regional settings that numbers are formatted and parsed with, the control word that a script reads and writes, the unit that compiled code computes floats with (`fpu`), and the routines that convert floats to text and back (`library`). The methods answer how Delphi's compiled code computes where the platforms differ. """ platform: Platform pointer_size: int variant_size: int extended_size: int parses_long_digits_as_infinity: bool """ Whether Windows reads a number without exponent beyond the range of a Double from a string as an infinity instead of failing to convert it. """ fpu: FPU library: Library def __init__(self, settings: FormatSettings, control: FPU): self.settings = settings self._control = control @property def control_word(self) -> int: """ The control word that `Get8087CW` reads and `Set8087CW` writes. """ return self._control.control @control_word.setter def control_word(self, value: int): self._control.control = value def real_arithmetic(self, op: Operation, a: Real, b: Real, fmt: Format = Format.Extended) -> Real: """ Computes with two floats and stores the result into a variable of the given format. """ fpu = self.fpu if op is Operation.Add: result = fpu.add(a, b) elif op is Operation.Subtract: result = fpu.subtract(a, b) elif op is Operation.Multiply: result = fpu.multiply(a, b) else: result = fpu.divide(a, b) return fpu.store(result, fmt) def currency(self, value: Real) -> Fraction: """ Converts a float to a Currency: the value times 10000, rounded as a result, and then to an integer as the unit rounds. On the x87, a value out of range signals an invalid operation and gives the smallest Currency. """ fpu = self.fpu return Fraction(fpu.integer(fpu.multiply(value, 10000.0)), 10000) def currency_to_real(self, value: Fraction) -> Real: """ Converts a Currency to a float: its units of 1/10000 divided by 10000. """ return self.fpu.divide(self.fpu.load(units(value)), 10000.0) def currency_arithmetic(self, op: Operation, a: Fraction, b: Fraction | int | Real) -> Fraction: """ Computes with a Currency and a Currency, an integer, or a float, and stores the result as a Currency. The unit loads the units of 1/10000 of the Currency, computes with the other operand, and stores the result as an integer, rounded as the unit rounds. A Currency operand counts in units, an integer in whole numbers, a float as its value. """ fpu = self.fpu left = fpu.load(units(a)) if isinstance(b, Fraction): right = fpu.load(units(b)) if op is Operation.Multiply: result = fpu.divide(fpu.multiply(left, right), 10000.0) elif op is Operation.Divide: result = fpu.multiply(fpu.divide(left, right), 10000.0) else: result = self.real_arithmetic(op, left, right) elif op is Operation.Add or op is Operation.Subtract: right = fpu.load(b * 10000) if isinstance(b, int) else fpu.multiply(b, 10000.0) result = self.real_arithmetic(op, left, right) else: result = self.real_arithmetic(op, left, fpu.load(b) if isinstance(b, int) else b) return Fraction(fpu.integer(result), 10000) @abc.abstractmethod def currency_of_integer(self, value: int) -> Fraction: """ Converts an integer to a Currency. """ @abc.abstractmethod def variant_currency(self, value: Real) -> Fraction: """ Converts the float of a Variant to a Currency; the caller checks the range. """ @abc.abstractmethod def real_divided_by_currency(self, a: Real, b: Fraction, fmt: Format) -> Real: """ Divides a float by a Currency and stores the result into a variable of the given format. """ @abc.abstractmethod def check_currency_divisor(self, b: Fraction): """ Raises the error of a division by a Currency of zero, if the platform raises one. """ @abc.abstractmethod def int64_division(self, a: int, b: int) -> tuple[int, int]: """ The quotient and the remainder of Int64 operands, truncated toward zero. """Ancestors
- abc.ABC
Subclasses
Class variables
var platform-
The type of the None singleton.
var pointer_size-
The type of the None singleton.
var variant_size-
The type of the None singleton.
var extended_size-
The type of the None singleton.
var parses_long_digits_as_infinity-
Whether Windows reads a number without exponent beyond the range of a Double from a string as an infinity instead of failing to convert it.
var fpu-
The type of the None singleton.
var library-
The type of the None singleton.
Instance variables
var control_word-
The control word that
Get8087CWreads andSet8087CWwrites.Expand source code Browse git
@property def control_word(self) -> int: """ The control word that `Get8087CW` reads and `Set8087CW` writes. """ return self._control.control
Methods
def real_arithmetic(self, op, a, b, fmt=Format.Extended)-
Computes with two floats and stores the result into a variable of the given format.
Expand source code Browse git
def real_arithmetic(self, op: Operation, a: Real, b: Real, fmt: Format = Format.Extended) -> Real: """ Computes with two floats and stores the result into a variable of the given format. """ fpu = self.fpu if op is Operation.Add: result = fpu.add(a, b) elif op is Operation.Subtract: result = fpu.subtract(a, b) elif op is Operation.Multiply: result = fpu.multiply(a, b) else: result = fpu.divide(a, b) return fpu.store(result, fmt) def currency(self, value)-
Converts a float to a Currency: the value times 10000, rounded as a result, and then to an integer as the unit rounds. On the x87, a value out of range signals an invalid operation and gives the smallest Currency.
Expand source code Browse git
def currency(self, value: Real) -> Fraction: """ Converts a float to a Currency: the value times 10000, rounded as a result, and then to an integer as the unit rounds. On the x87, a value out of range signals an invalid operation and gives the smallest Currency. """ fpu = self.fpu return Fraction(fpu.integer(fpu.multiply(value, 10000.0)), 10000) def currency_to_real(self, value)-
Converts a Currency to a float: its units of 1/10000 divided by 10000.
Expand source code Browse git
def currency_to_real(self, value: Fraction) -> Real: """ Converts a Currency to a float: its units of 1/10000 divided by 10000. """ return self.fpu.divide(self.fpu.load(units(value)), 10000.0) def currency_arithmetic(self, op, a, b)-
Computes with a Currency and a Currency, an integer, or a float, and stores the result as a Currency. The unit loads the units of 1/10000 of the Currency, computes with the other operand, and stores the result as an integer, rounded as the unit rounds. A Currency operand counts in units, an integer in whole numbers, a float as its value.
Expand source code Browse git
def currency_arithmetic(self, op: Operation, a: Fraction, b: Fraction | int | Real) -> Fraction: """ Computes with a Currency and a Currency, an integer, or a float, and stores the result as a Currency. The unit loads the units of 1/10000 of the Currency, computes with the other operand, and stores the result as an integer, rounded as the unit rounds. A Currency operand counts in units, an integer in whole numbers, a float as its value. """ fpu = self.fpu left = fpu.load(units(a)) if isinstance(b, Fraction): right = fpu.load(units(b)) if op is Operation.Multiply: result = fpu.divide(fpu.multiply(left, right), 10000.0) elif op is Operation.Divide: result = fpu.multiply(fpu.divide(left, right), 10000.0) else: result = self.real_arithmetic(op, left, right) elif op is Operation.Add or op is Operation.Subtract: right = fpu.load(b * 10000) if isinstance(b, int) else fpu.multiply(b, 10000.0) result = self.real_arithmetic(op, left, right) else: result = self.real_arithmetic(op, left, fpu.load(b) if isinstance(b, int) else b) return Fraction(fpu.integer(result), 10000) def currency_of_integer(self, value)-
Converts an integer to a Currency.
Expand source code Browse git
@abc.abstractmethod def currency_of_integer(self, value: int) -> Fraction: """ Converts an integer to a Currency. """ def variant_currency(self, value)-
Converts the float of a Variant to a Currency; the caller checks the range.
Expand source code Browse git
@abc.abstractmethod def variant_currency(self, value: Real) -> Fraction: """ Converts the float of a Variant to a Currency; the caller checks the range. """ def real_divided_by_currency(self, a, b, fmt)-
Divides a float by a Currency and stores the result into a variable of the given format.
Expand source code Browse git
@abc.abstractmethod def real_divided_by_currency(self, a: Real, b: Fraction, fmt: Format) -> Real: """ Divides a float by a Currency and stores the result into a variable of the given format. """ def check_currency_divisor(self, b)-
Raises the error of a division by a Currency of zero, if the platform raises one.
Expand source code Browse git
@abc.abstractmethod def check_currency_divisor(self, b: Fraction): """ Raises the error of a division by a Currency of zero, if the platform raises one. """ def int64_division(self, a, b)-
The quotient and the remainder of Int64 operands, truncated toward zero.
Expand source code Browse git
@abc.abstractmethod def int64_division(self, a: int, b: int) -> tuple[int, int]: """ The quotient and the remainder of Int64 operands, truncated toward zero. """
class Win32 (settings, control_word)-
Delphi's 32-bit code: the x87 computes under the control word of the script, and the library computes on the same x87.
Expand source code Browse git
class Win32(Runtime): """ Delphi's 32-bit code: the x87 computes under the control word of the script, and the library computes on the same x87. """ platform = Platform.Win32 pointer_size = 4 variant_size = 16 extended_size = 10 parses_long_digits_as_infinity = False def __init__(self, settings: FormatSettings, control_word: int): fpu = FPU(control_word) super().__init__(settings, fpu) self.fpu = fpu self.library = Library( unit=fpu, digits=18, table_end=15, exponent_limit=EXPONENT_LIMIT, minimum_power_is_infinite=False, ) def currency_of_integer(self, value: int) -> Fraction: """ The x87 loads the integer and converts it as a float: a value out of range signals an invalid operation and gives the smallest Currency. """ return self.currency(self.fpu.load(value)) def variant_currency(self, value: Real) -> Fraction: return self.currency(value) def real_divided_by_currency(self, a: Real, b: Fraction, fmt: Format) -> Real: """ The x87 multiplies the float by 10000 and divides by the units of the Currency. """ fpu = self.fpu return fpu.store(fpu.divide(fpu.multiply(a, 10000.0), fpu.load(units(b))), fmt) def check_currency_divisor(self, b: Fraction): return def int64_division(self, a: int, b: int) -> tuple[int, int]: """ The run-time library divides; the smallest Int64 divided by -1 gives itself. """ return truncated_division(a, b)Ancestors
- Runtime
- abc.ABC
Methods
def currency_of_integer(self, value)-
The x87 loads the integer and converts it as a float: a value out of range signals an invalid operation and gives the smallest Currency.
Expand source code Browse git
def currency_of_integer(self, value: int) -> Fraction: """ The x87 loads the integer and converts it as a float: a value out of range signals an invalid operation and gives the smallest Currency. """ return self.currency(self.fpu.load(value)) def real_divided_by_currency(self, a, b, fmt)-
The x87 multiplies the float by 10000 and divides by the units of the Currency.
Expand source code Browse git
def real_divided_by_currency(self, a: Real, b: Fraction, fmt: Format) -> Real: """ The x87 multiplies the float by 10000 and divides by the units of the Currency. """ fpu = self.fpu return fpu.store(fpu.divide(fpu.multiply(a, 10000.0), fpu.load(units(b))), fmt) def int64_division(self, a, b)-
The run-time library divides; the smallest Int64 divided by -1 gives itself.
Expand source code Browse git
def int64_division(self, a: int, b: int) -> tuple[int, int]: """ The run-time library divides; the smallest Int64 divided by -1 gives itself. """ return truncated_division(a, b)
Inherited members
class Win64 (settings)-
Delphi's 64-bit code: SSE computes every float as a Double. The library computes as an x87 with the control word of 64-bit code, whatever the script sets; its table of powers of ten ends at
10 ** 256and itsValhas no limit on the exponent.Expand source code Browse git
class Win64(Runtime): """ Delphi's 64-bit code: SSE computes every float as a Double. The library computes as an x87 with the control word of 64-bit code, whatever the script sets; its table of powers of ten ends at `10 ** 256` and its `Val` has no limit on the exponent. """ platform = Platform.Win64 pointer_size = 8 variant_size = 24 extended_size = 8 parses_long_digits_as_infinity = True def __init__(self, settings: FormatSettings): super().__init__(settings, FPU(WIN64_CONTROL_WORD)) self.fpu = SSE() self.library = Library( unit=FPU(WIN64_CONTROL_WORD), digits=17, table_end=8, exponent_limit=None, minimum_power_is_infinite=True, ) def currency_of_integer(self, value: int) -> Fraction: """ 64-bit code multiplies the integer by 10000 as a 64-bit integer, which wraps around. """ return Fraction(_signed64(value * 10000), 10000) def variant_currency(self, value: Real) -> Fraction: """ Windows' 64-bit conversion rounds the value times 10000 as an x87 with every exception masked does: at 64 bits of mantissa, then to the nearest integer. """ fpu = _NEAREST return Fraction(fpu.integer(fpu.multiply(value, 10000.0)), 10000) def currency_arithmetic(self, op: Operation, a: Fraction, b: Fraction | int | Real) -> Fraction: """ Two Currency values, and a Currency and an integer that are added or subtracted, compute in units as 64-bit integers: sums wrap around, a product out of range raises an overflow, and a quotient by zero a division by zero. Products and quotients round half to even. What a quotient out of range gives is not known. """ if not isinstance(b, Fraction) and not (isinstance(b, int) and op in (Operation.Add, Operation.Subtract)): return super().currency_arithmetic(op, a, b) left = units(a) right = units(b) if isinstance(b, Fraction) else b * 10000 if op is Operation.Add: return Fraction(_signed64(left + right), 10000) if op is Operation.Subtract: return Fraction(_signed64(left - right), 10000) if op is Operation.Multiply: result = round(Fraction(left * right, 10000)) if result not in _INT64: raise EIntOverflow return Fraction(result, 10000) if right == 0: raise EZeroDivide result = round(Fraction(left * 10000, right)) if result not in _INT64: raise Undefined('64-bit Delphi divides two Currency values whose quotient is out of range in a way that is not modelled.') return Fraction(result, 10000) def real_divided_by_currency(self, a: Real, b: Fraction, fmt: Format) -> Real: """ 64-bit code divides the float by the units of the Currency and multiplies by 10000. """ fpu = self.fpu return fpu.store(fpu.multiply(fpu.divide(a, fpu.load(units(b))), 10000.0), fmt) def check_currency_divisor(self, b: Fraction): if b == 0: raise EZeroDivide def int64_division(self, a: int, b: int) -> tuple[int, int]: """ The processor divides; the smallest Int64 divided by -1 overflows. """ if a == -0x8000000000000000 and b == -1: raise EIntOverflow return truncated_division(a, b)Ancestors
- Runtime
- abc.ABC
Methods
def currency_of_integer(self, value)-
64-bit code multiplies the integer by 10000 as a 64-bit integer, which wraps around.
Expand source code Browse git
def currency_of_integer(self, value: int) -> Fraction: """ 64-bit code multiplies the integer by 10000 as a 64-bit integer, which wraps around. """ return Fraction(_signed64(value * 10000), 10000) def variant_currency(self, value)-
Windows' 64-bit conversion rounds the value times 10000 as an x87 with every exception masked does: at 64 bits of mantissa, then to the nearest integer.
Expand source code Browse git
def variant_currency(self, value: Real) -> Fraction: """ Windows' 64-bit conversion rounds the value times 10000 as an x87 with every exception masked does: at 64 bits of mantissa, then to the nearest integer. """ fpu = _NEAREST return Fraction(fpu.integer(fpu.multiply(value, 10000.0)), 10000) def currency_arithmetic(self, op, a, b)-
Two Currency values, and a Currency and an integer that are added or subtracted, compute in units as 64-bit integers: sums wrap around, a product out of range raises an overflow, and a quotient by zero a division by zero. Products and quotients round half to even. What a quotient out of range gives is not known.
Expand source code Browse git
def currency_arithmetic(self, op: Operation, a: Fraction, b: Fraction | int | Real) -> Fraction: """ Two Currency values, and a Currency and an integer that are added or subtracted, compute in units as 64-bit integers: sums wrap around, a product out of range raises an overflow, and a quotient by zero a division by zero. Products and quotients round half to even. What a quotient out of range gives is not known. """ if not isinstance(b, Fraction) and not (isinstance(b, int) and op in (Operation.Add, Operation.Subtract)): return super().currency_arithmetic(op, a, b) left = units(a) right = units(b) if isinstance(b, Fraction) else b * 10000 if op is Operation.Add: return Fraction(_signed64(left + right), 10000) if op is Operation.Subtract: return Fraction(_signed64(left - right), 10000) if op is Operation.Multiply: result = round(Fraction(left * right, 10000)) if result not in _INT64: raise EIntOverflow return Fraction(result, 10000) if right == 0: raise EZeroDivide result = round(Fraction(left * 10000, right)) if result not in _INT64: raise Undefined('64-bit Delphi divides two Currency values whose quotient is out of range in a way that is not modelled.') return Fraction(result, 10000) def real_divided_by_currency(self, a, b, fmt)-
64-bit code divides the float by the units of the Currency and multiplies by 10000.
Expand source code Browse git
def real_divided_by_currency(self, a: Real, b: Fraction, fmt: Format) -> Real: """ 64-bit code divides the float by the units of the Currency and multiplies by 10000. """ fpu = self.fpu return fpu.store(fpu.multiply(fpu.divide(a, fpu.load(units(b))), 10000.0), fmt) def int64_division(self, a, b)-
The processor divides; the smallest Int64 divided by -1 overflows.
Expand source code Browse git
def int64_division(self, a: int, b: int) -> tuple[int, int]: """ The processor divides; the smallest Int64 divided by -1 overflows. """ if a == -0x8000000000000000 and b == -1: raise EIntOverflow return truncated_division(a, b)
Inherited members