mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 15:42:02 +00:00
Make reading of Decimal more compatible with other DBMS
This commit is contained in:
parent
7b2b726c17
commit
408fc241d6
@ -134,3 +134,14 @@ inline __int128 exp10_i128(int x)
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// intExp10 returning the type T.
|
||||||
|
template <typename T>
|
||||||
|
inline T intExp10OfSize(int x)
|
||||||
|
{
|
||||||
|
if constexpr (sizeof(T) <= 8)
|
||||||
|
return intExp10(x);
|
||||||
|
else
|
||||||
|
return common::exp10_i128(x);
|
||||||
|
}
|
||||||
|
@ -31,8 +31,13 @@ template <> struct FunctionUnaryArithmeticMonotonicity<NameIntExp10>
|
|||||||
static bool has() { return true; }
|
static bool has() { return true; }
|
||||||
static IFunction::Monotonicity get(const Field & left, const Field & right)
|
static IFunction::Monotonicity get(const Field & left, const Field & right)
|
||||||
{
|
{
|
||||||
Float64 left_float = left.isNull() ? -std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), left);
|
Float64 left_float = left.isNull()
|
||||||
Float64 right_float = right.isNull() ? std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), right);
|
? -std::numeric_limits<Float64>::infinity()
|
||||||
|
: applyVisitor(FieldVisitorConvertToNumber<Float64>(), left);
|
||||||
|
|
||||||
|
Float64 right_float = right.isNull()
|
||||||
|
? std::numeric_limits<Float64>::infinity()
|
||||||
|
: applyVisitor(FieldVisitorConvertToNumber<Float64>(), right);
|
||||||
|
|
||||||
if (left_float < 0 || right_float > 19)
|
if (left_float < 0 || right_float > 19)
|
||||||
return {};
|
return {};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <IO/ReadHelpers.h>
|
#include <IO/ReadHelpers.h>
|
||||||
|
#include <Common/intExp.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
@ -79,23 +80,35 @@ inline bool readDigits(ReadBuffer & buf, T & x, uint32_t & digits, int32_t & exp
|
|||||||
|
|
||||||
++places; // num zeroes before + current digit
|
++places; // num zeroes before + current digit
|
||||||
if (digits + places > max_digits)
|
if (digits + places > max_digits)
|
||||||
|
{
|
||||||
|
if (after_point)
|
||||||
|
{
|
||||||
|
/// Simply cut excessive digits.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if constexpr (_throw_on_error)
|
if constexpr (_throw_on_error)
|
||||||
throw Exception("Too many digits (" + std::to_string(digits + places) + " > " + std::to_string(max_digits)
|
throw Exception("Too many digits (" + std::to_string(digits + places) + " > " + std::to_string(max_digits)
|
||||||
+ ") in decimal value", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
+ ") in decimal value", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
digits += places;
|
digits += places;
|
||||||
if (after_point)
|
if (after_point)
|
||||||
exponent -= places;
|
exponent -= places;
|
||||||
|
|
||||||
// TODO: accurate shift10 for big integers
|
// TODO: accurate shift10 for big integers
|
||||||
for (; places; --places)
|
x *= intExp10OfSize<T>(places);
|
||||||
x *= 10;
|
places = 0;
|
||||||
|
|
||||||
x += (byte - '0');
|
x += (byte - '0');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
case 'e': [[fallthrough]];
|
case 'e': [[fallthrough]];
|
||||||
case 'E':
|
case 'E':
|
||||||
{
|
{
|
||||||
@ -144,10 +157,12 @@ inline void readDecimalText(ReadBuffer & buf, T & x, uint32_t precision, uint32_
|
|||||||
digits, x, exponent, scale, precision), ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
digits, x, exponent, scale, precision), ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||||
|
|
||||||
if (static_cast<int32_t>(scale) + exponent < 0)
|
if (static_cast<int32_t>(scale) + exponent < 0)
|
||||||
throw Exception(fmt::format(
|
{
|
||||||
"Decimal value has too large number of digits after point: {} digits were read: {}e{}."
|
/// Too many digits after point. Just cut off excessive digits.
|
||||||
" Expected to read decimal with scale {} and precision {}",
|
x.value /= intExp10OfSize<T>(-exponent - scale);
|
||||||
digits, x, exponent, scale, precision), ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
scale = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
scale += exponent;
|
scale += exponent;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
1.10000
|
||||||
|
1.12345
|
||||||
|
1.12345
|
||||||
|
1.12345
|
||||||
|
1.12345
|
||||||
|
1.12345
|
||||||
|
12345.10000
|
||||||
|
123456789123.10000
|
||||||
|
1234567891234.10000
|
||||||
|
1234567891234.12345
|
||||||
|
1.12345
|
||||||
|
1.12345
|
||||||
|
1.12344
|
||||||
|
1.12345
|
@ -0,0 +1,24 @@
|
|||||||
|
SELECT CAST('1.1' AS Decimal(10, 5));
|
||||||
|
SELECT CAST('1.12345' AS Decimal(10, 5));
|
||||||
|
SELECT CAST('1.123451' AS Decimal(10, 5));
|
||||||
|
SELECT CAST('1.1234511111' AS Decimal(10, 5));
|
||||||
|
SELECT CAST('1.12345111111' AS Decimal(10, 5));
|
||||||
|
SELECT CAST('1.12345111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111' AS Decimal(10, 5));
|
||||||
|
SELECT CAST('12345.1' AS Decimal(10, 5));
|
||||||
|
|
||||||
|
-- Actually our decimal can contain more than 10 digits for free.
|
||||||
|
SELECT CAST('123456789123.1' AS Decimal(10, 5));
|
||||||
|
SELECT CAST('1234567891234.1' AS Decimal(10, 5));
|
||||||
|
SELECT CAST('1234567891234.12345111' AS Decimal(10, 5));
|
||||||
|
-- But it's just Decimal64, so there is the limit.
|
||||||
|
SELECT CAST('12345678912345.1' AS Decimal(10, 5)); -- { serverError 69 }
|
||||||
|
|
||||||
|
-- The rounding may work in unexpected way: this is just integer rounding.
|
||||||
|
-- We can improve it but here is the current behaviour:
|
||||||
|
SELECT CAST('1.123455' AS Decimal(10, 5));
|
||||||
|
SELECT CAST('1.123456' AS Decimal(10, 5));
|
||||||
|
SELECT CAST('1.123445' AS Decimal(10, 5)); -- Check if suddenly banker's rounding will be implemented.
|
||||||
|
|
||||||
|
CREATE TEMPORARY TABLE test (x Decimal(10, 5));
|
||||||
|
INSERT INTO test VALUES (1.12345111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111);
|
||||||
|
SELECT * FROM test;
|
Loading…
Reference in New Issue
Block a user