LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
decimal.h
Go to the documentation of this file.
1
/*
2
* Copyright (c) 2012-2026, QORE Lab, Imperial College London
3
* All rights reserved.
4
*/
5
#ifndef LINE_UTIL_DECIMAL_H
6
#define LINE_UTIL_DECIMAL_H
7
8
/**
9
* @file
10
* @ingroup line_util
11
* Decimal literal -> T, without a detour through double when T is exact.
12
*
13
* This is the boundary at which a model file becomes numbers, and it is the
14
* one place where "exact arithmetic" has to decide what it is exact ABOUT. A
15
* host demand written `0.01` in an .lqnx file denotes the rational 1/100. The
16
* double nearest to it is 0.01000000000000000020816681711721685...; routing
17
* that literal through `num_traits<Rational>::from_double` would make the
18
* exact backend compute, with no rounding at all, the answer to a model that
19
* is not the one on disk. So the exact backend parses the decimal digits
20
* directly into num/10^k, and the inexact backends keep strtod, which is what
21
* MATLAB's str2double and Java's Double.parseDouble also do.
22
*
23
* The consequence to keep in mind when comparing the two runs: they are not
24
* two evaluations of one arithmetic problem, they are the exact solution of
25
* the declared model versus the floating-point solution of its double
26
* rounding. Their difference is bounded below by the input rounding, of order
27
* 1e-17 relative here, and any larger gap is accumulated error in the solver.
28
*/
29
30
#include <cctype>
31
#include <cstdlib>
32
#include <string>
33
34
#include "
line/num/number.h
"
35
#include "
line/util/error.h
"
36
37
namespace
line
{
38
39
namespace
detail {
40
41
/** Split a decimal literal into (sign, digits, exponent) with value = sign * digits * 10^exp. */
42
inline
bool
decimal_parts(
const
std::string& s,
bool
& neg, std::string& digits,
long
& exp10) {
43
std::size_t i = 0;
44
while
(i < s.size() && std::isspace(
static_cast<
unsigned
char
>
(s[i]))) ++i;
45
neg =
false
;
46
if
(i < s.size() && (s[i] ==
'+'
|| s[i] ==
'-'
)) neg = (s[i++] ==
'-'
);
47
48
digits.clear();
49
exp10 = 0;
50
bool
any =
false
;
51
while
(i < s.size() && std::isdigit(
static_cast<
unsigned
char
>
(s[i]))) {
52
digits.push_back(s[i++]);
53
any =
true
;
54
}
55
if
(i < s.size() && s[i] ==
'.'
) {
56
++i;
57
while
(i < s.size() && std::isdigit(
static_cast<
unsigned
char
>
(s[i]))) {
58
digits.push_back(s[i++]);
59
--exp10;
60
any =
true
;
61
}
62
}
63
if
(!any)
return
false
;
64
if
(i < s.size() && (s[i] ==
'e'
|| s[i] ==
'E'
)) {
65
++i;
66
bool
eneg =
false
;
67
if
(i < s.size() && (s[i] ==
'+'
|| s[i] ==
'-'
)) eneg = (s[i++] ==
'-'
);
68
long
e = 0;
69
bool
anye =
false
;
70
while
(i < s.size() && std::isdigit(
static_cast<
unsigned
char
>
(s[i]))) {
71
e = e * 10 + (s[i++] -
'0'
);
72
anye =
true
;
73
}
74
if
(!anye)
return
false
;
75
exp10 += eneg ? -e : e;
76
}
77
while
(i < s.size() && std::isspace(
static_cast<
unsigned
char
>
(s[i]))) ++i;
78
return
i == s.size();
79
}
80
81
}
// namespace detail
82
83
/** The literal as an exact rational, num/10^k with no rounding. */
84
inline
Rational
rational_from_decimal
(
const
std::string& s) {
85
bool
neg =
false
;
86
std::string digits;
87
long
exp10 = 0;
88
if
(!detail::decimal_parts(s, neg, digits, exp10))
89
throw
InputError
(
"num_from_decimal: '"
+ s +
"' is not a decimal literal"
);
90
BigInt
mant = 0;
91
for
(
char
c : digits) mant = mant * 10 + (c -
'0'
);
92
BigInt
num = mant;
93
BigInt
den = 1;
94
if
(exp10 >= 0) {
95
for
(
long
k = 0; k < exp10; ++k) num *= 10;
96
}
else
{
97
for
(
long
k = 0; k < -exp10; ++k) den *= 10;
98
}
99
Rational
r(num, den);
100
return
neg ?
Rational
(-r) : r;
101
}
102
103
/**
104
* Parse a decimal literal into T.
105
*
106
* Rational reconstructs num/10^k from the digits; every other backend uses
107
* strtod, matching what MATLAB's str2double and Java's Double.parseDouble do.
108
*/
109
template
<
class
T>
110
T
num_from_decimal
(
const
std::string& s) {
111
return
num_traits<T>::from_double
(std::strtod(s.c_str(),
nullptr
));
112
}
113
114
template
<>
115
inline
Rational
num_from_decimal<Rational>
(
const
std::string& s) {
116
return
rational_from_decimal
(s);
117
}
118
119
/** Parse a decimal literal as a plain double (multiplicities, populations, tolerances). */
120
inline
double
dbl_from_decimal
(
const
std::string& s,
double
fallback) {
121
if
(s.empty())
return
fallback;
122
const
char
* p = s.c_str();
123
char
* end =
nullptr
;
124
const
double
v = std::strtod(p, &end);
125
if
(end == p)
return
fallback;
126
return
v;
127
}
128
129
}
// namespace line
130
131
#endif
// LINE_UTIL_DECIMAL_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
error.h
The exception types the port throws.
line
Definition
aoi_dist2ph.h:52
line::dbl_from_decimal
double dbl_from_decimal(const std::string &s, double fallback)
Parse a decimal literal as a plain double (multiplicities, populations, tolerances).
Definition
decimal.h:120
line::BigInt
boost::multiprecision::cpp_int BigInt
Definition
number.h:78
line::num_from_decimal
T num_from_decimal(const std::string &s)
Parse a decimal literal into T.
Definition
decimal.h:110
line::rational_from_decimal
Rational rational_from_decimal(const std::string &s)
The literal as an exact rational, num/10^k with no rounding.
Definition
decimal.h:84
line::Rational
boost::multiprecision::number< boost::multiprecision::cpp_rational_backend, boost::multiprecision::et_off > Rational
Definition
number.h:76
number.h
Number-type abstraction for the templated API port.
line::num_traits
Definition
number.h:111
include
line
util
decimal.h
Generated by
1.18.0