mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 23:52:03 +00:00
dbms: SAMPLE ... OFFSET development: using rational numbers [#METR-18847].
This commit is contained in:
parent
f0a17a377a
commit
2d00e5d84f
@ -60,7 +60,4 @@ template <> struct TypeName<Float32> { static std::string get() { return "Float
|
|||||||
template <> struct TypeName<Float64> { static std::string get() { return "Float64"; } };
|
template <> struct TypeName<Float64> { static std::string get() { return "Float64"; } };
|
||||||
template <> struct TypeName<String> { static std::string get() { return "String"; } };
|
template <> struct TypeName<String> { static std::string get() { return "String"; } };
|
||||||
|
|
||||||
/// Эти типы не поддерживаются СУБД. Но используются в других местах.
|
|
||||||
template <> struct TypeName<long double>{ static std::string get() { return "long double"; } };
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -86,9 +86,6 @@ struct Settings
|
|||||||
M(SettingTotalsMode, totals_mode, TotalsMode::AFTER_HAVING_EXCLUSIVE) \
|
M(SettingTotalsMode, totals_mode, TotalsMode::AFTER_HAVING_EXCLUSIVE) \
|
||||||
M(SettingFloat, totals_auto_threshold, 0.5) \
|
M(SettingFloat, totals_auto_threshold, 0.5) \
|
||||||
\
|
\
|
||||||
/** Сэмплирование по умолчанию. Если равно 1, то отключено. */ \
|
|
||||||
M(SettingFloat, default_sample, 1.0) \
|
|
||||||
\
|
|
||||||
/** Включена ли компиляция запросов. */ \
|
/** Включена ли компиляция запросов. */ \
|
||||||
M(SettingBool, compile, false) \
|
M(SettingBool, compile, false) \
|
||||||
/** Количество одинаковых по структуре запросов перед тем, как инициируется их компиляция. */ \
|
/** Количество одинаковых по структуре запросов перед тем, как инициируется их компиляция. */ \
|
||||||
|
43
dbms/include/DB/Parsers/ASTSampleRatio.h
Normal file
43
dbms/include/DB/Parsers/ASTSampleRatio.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <DB/Parsers/IAST.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
/** Коэффициент сэмплирования вида 0.1 или 1/10.
|
||||||
|
* Важно сохранять его как рациональное число без преобразования в IEEE-754.
|
||||||
|
*/
|
||||||
|
class ASTSampleRatio : public IAST
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using BigNum = __uint128_t; /// Должен вмещать в себя результат перемножения двух UInt64.
|
||||||
|
|
||||||
|
struct Rational
|
||||||
|
{
|
||||||
|
BigNum numerator = 0;
|
||||||
|
BigNum denominator = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
Rational ratio;
|
||||||
|
|
||||||
|
ASTSampleRatio() = default;
|
||||||
|
ASTSampleRatio(const StringRange range_) : IAST(range_) {}
|
||||||
|
ASTSampleRatio(const StringRange range_, Rational & ratio_) : IAST(range_), ratio(ratio_) {}
|
||||||
|
|
||||||
|
String getID() const override { return "SampleRatio_" + toString(ratio); }
|
||||||
|
|
||||||
|
ASTPtr clone() const override { return new ASTSampleRatio(*this); }
|
||||||
|
|
||||||
|
static String toString(BigNum num);
|
||||||
|
static String toString(Rational ratio);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
|
||||||
|
{
|
||||||
|
settings.ostr << toString(ratio);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
19
dbms/include/DB/Parsers/ParserSampleRatio.h
Normal file
19
dbms/include/DB/Parsers/ParserSampleRatio.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <DB/Parsers/IParserBase.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
/** Коэффициент сэмплирования вида 0.1 или 1/10.
|
||||||
|
* Парсится как рациональное число без преобразования в IEEE-754.
|
||||||
|
*/
|
||||||
|
class ParserSampleRatio : public IParserBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
const char * getName() const { return "Sample ratio or offset"; }
|
||||||
|
bool parseImpl(Pos & pos, Pos end, ASTPtr & node, Pos & max_parsed_pos, Expected & expected);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -698,12 +698,6 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns()
|
|||||||
interpreter_subquery->ignoreWithTotals();
|
interpreter_subquery->ignoreWithTotals();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// если в настройках установлен default_sample != 1, то все запросы выполняем с сэмплингом
|
|
||||||
/// если таблица не поддерживает сэмплинг получим исключение
|
|
||||||
/// поэтому запросы типа SHOW TABLES работать с включенном default_sample не будут
|
|
||||||
if (!query.sample_size && settings.default_sample != 1)
|
|
||||||
query.sample_size = new ASTLiteral(StringRange(), Float64(settings.default_sample));
|
|
||||||
|
|
||||||
if (query.sample_size && (!storage || !storage->supportsSampling()))
|
if (query.sample_size && (!storage || !storage->supportsSampling()))
|
||||||
throw Exception("Illegal SAMPLE: table doesn't support sampling", ErrorCodes::SAMPLING_NOT_SUPPORTED);
|
throw Exception("Illegal SAMPLE: table doesn't support sampling", ErrorCodes::SAMPLING_NOT_SUPPORTED);
|
||||||
|
|
||||||
|
40
dbms/src/Parsers/ASTSampleRatio.cpp
Normal file
40
dbms/src/Parsers/ASTSampleRatio.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include <DB/Parsers/ASTSampleRatio.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
String ASTSampleRatio::toString(BigNum num)
|
||||||
|
{
|
||||||
|
if (num == 0)
|
||||||
|
return "0";
|
||||||
|
|
||||||
|
static const size_t MAX_WIDTH = 40;
|
||||||
|
|
||||||
|
char tmp[MAX_WIDTH];
|
||||||
|
|
||||||
|
char * pos;
|
||||||
|
for (pos = tmp + MAX_WIDTH - 1; num != 0; --pos)
|
||||||
|
{
|
||||||
|
*pos = '0' + num % 10;
|
||||||
|
num /= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
++pos;
|
||||||
|
|
||||||
|
return String(pos, tmp + MAX_WIDTH - pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String ASTSampleRatio::toString(Rational ratio)
|
||||||
|
{
|
||||||
|
if (ratio.denominator == 1)
|
||||||
|
return toString(ratio.numerator);
|
||||||
|
else
|
||||||
|
return toString(ratio.numerator) + " / " + toString(ratio.denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
128
dbms/src/Parsers/ParserSampleRatio.cpp
Normal file
128
dbms/src/Parsers/ParserSampleRatio.cpp
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#include <DB/Parsers/CommonParsers.h>
|
||||||
|
#include <DB/Parsers/ParserSampleRatio.h>
|
||||||
|
#include <DB/Parsers/ASTSampleRatio.h>
|
||||||
|
#include <DB/IO/ReadHelpers.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
static bool parseDecimal(IParser::Pos & pos, IParser::Pos end, ASTSampleRatio::Rational & res, IParser::Pos & max_parsed_pos)
|
||||||
|
{
|
||||||
|
ParserWhiteSpaceOrComments ws;
|
||||||
|
ws.ignore(pos, end);
|
||||||
|
|
||||||
|
UInt64 num_before = 0;
|
||||||
|
UInt64 num_after = 0;
|
||||||
|
Int64 exponent = 0;
|
||||||
|
|
||||||
|
IParser::Pos pos_after_first_num = tryReadIntText(num_before, pos, end);
|
||||||
|
|
||||||
|
bool has_num_before_point = pos_after_first_num > pos;
|
||||||
|
pos = pos_after_first_num;
|
||||||
|
bool has_point = pos < end && *pos == '.';
|
||||||
|
|
||||||
|
if (has_point)
|
||||||
|
++pos;
|
||||||
|
|
||||||
|
if (!has_num_before_point && !has_point)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t number_of_digits_after_point = 0;
|
||||||
|
|
||||||
|
if (has_point)
|
||||||
|
{
|
||||||
|
IParser::Pos pos_after_second_num = tryReadIntText(num_after, pos, end);
|
||||||
|
number_of_digits_after_point = pos_after_second_num - pos;
|
||||||
|
pos = pos_after_second_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_exponent = pos < end && (*pos == 'e' || *pos == 'E');
|
||||||
|
|
||||||
|
if (has_exponent)
|
||||||
|
{
|
||||||
|
++pos;
|
||||||
|
IParser::Pos pos_after_exponent = tryReadIntText(exponent, pos, end);
|
||||||
|
|
||||||
|
if (pos_after_exponent == pos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pos = pos_after_exponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.numerator = num_before * exp10(number_of_digits_after_point) + num_after;
|
||||||
|
res.denominator = exp10(number_of_digits_after_point);
|
||||||
|
|
||||||
|
if (exponent > 0)
|
||||||
|
res.numerator *= exp10(exponent);
|
||||||
|
if (exponent < 0)
|
||||||
|
res.denominator *= exp10(-exponent);
|
||||||
|
|
||||||
|
/// NOTE Удаление общих степеней десяти из числителя и знаменателя - не нужно.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Возможные варианты:
|
||||||
|
*
|
||||||
|
* 12345
|
||||||
|
* - целое число
|
||||||
|
*
|
||||||
|
* 0.12345
|
||||||
|
* .12345
|
||||||
|
* 0.
|
||||||
|
* - дробь в обычной десятичной записи
|
||||||
|
*
|
||||||
|
* 1.23e-1
|
||||||
|
* - дробь в инженерной десятичной записи
|
||||||
|
*
|
||||||
|
* 123 / 456
|
||||||
|
* - дробь с произвольным знаменателем
|
||||||
|
*
|
||||||
|
* На всякий случай, в числителе и знаменателе дроби, поддерживаем предыдущие случаи.
|
||||||
|
* Пример:
|
||||||
|
* 123.0 / 456e0
|
||||||
|
*/
|
||||||
|
bool ParserSampleRatio::parseImpl(IParser::Pos & pos, IParser::Pos end, ASTPtr & node, IParser::Pos & max_parsed_pos, Expected & expected)
|
||||||
|
{
|
||||||
|
auto begin = pos;
|
||||||
|
|
||||||
|
ParserWhiteSpaceOrComments ws;
|
||||||
|
|
||||||
|
ASTSampleRatio::Rational numerator;
|
||||||
|
ASTSampleRatio::Rational denominator;
|
||||||
|
ASTSampleRatio::Rational res;
|
||||||
|
|
||||||
|
ws.ignore(pos, end);
|
||||||
|
|
||||||
|
if (!parseDecimal(pos, end, numerator, max_parsed_pos))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ws.ignore(pos, end);
|
||||||
|
|
||||||
|
bool has_slash = pos < end && *pos == '/';
|
||||||
|
|
||||||
|
if (has_slash)
|
||||||
|
{
|
||||||
|
++pos;
|
||||||
|
ws.ignore(pos, end);
|
||||||
|
|
||||||
|
if (!parseDecimal(pos, end, denominator, max_parsed_pos))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
res.numerator = numerator.numerator * denominator.denominator;
|
||||||
|
res.denominator = numerator.denominator * denominator.numerator;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = numerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.ignore(pos, end);
|
||||||
|
|
||||||
|
node = new ASTSampleRatio(StringRange(begin, pos), res);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -6,6 +6,7 @@
|
|||||||
#include <DB/Parsers/ExpressionListParsers.h>
|
#include <DB/Parsers/ExpressionListParsers.h>
|
||||||
#include <DB/Parsers/ParserJoin.h>
|
#include <DB/Parsers/ParserJoin.h>
|
||||||
#include <DB/Parsers/ParserSetQuery.h>
|
#include <DB/Parsers/ParserSetQuery.h>
|
||||||
|
#include <DB/Parsers/ParserSampleRatio.h>
|
||||||
#include <DB/Parsers/ParserSelectQuery.h>
|
#include <DB/Parsers/ParserSelectQuery.h>
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
@ -156,9 +157,9 @@ bool ParserSelectQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, Pos & max_p
|
|||||||
{
|
{
|
||||||
ws.ignore(pos, end);
|
ws.ignore(pos, end);
|
||||||
|
|
||||||
ParserNumber num;
|
ParserSampleRatio ratio;
|
||||||
|
|
||||||
if (!num.parse(pos, end, select_query->sample_size, max_parsed_pos, expected))
|
if (!ratio.parse(pos, end, select_query->sample_size, max_parsed_pos, expected))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ws.ignore(pos, end);
|
ws.ignore(pos, end);
|
||||||
@ -168,9 +169,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, Pos & max_p
|
|||||||
{
|
{
|
||||||
ws.ignore(pos, end);
|
ws.ignore(pos, end);
|
||||||
|
|
||||||
ParserNumber num;
|
if (!ratio.parse(pos, end, select_query->sample_offset, max_parsed_pos, expected))
|
||||||
|
|
||||||
if (!num.parse(pos, end, select_query->sample_offset, max_parsed_pos, expected))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ws.ignore(pos, end);
|
ws.ignore(pos, end);
|
||||||
|
@ -210,7 +210,7 @@ public:
|
|||||||
|
|
||||||
const MergeTreeSettings & settings = context.getMergeTreeSettings();
|
const MergeTreeSettings & settings = context.getMergeTreeSettings();
|
||||||
|
|
||||||
bool ok = true;
|
bool ok = /*true*/false;
|
||||||
std::stringstream message;
|
std::stringstream message;
|
||||||
|
|
||||||
for (const auto & db : replicated_tables)
|
for (const auto & db : replicated_tables)
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
#include <boost/rational.hpp> /// Для вычислений, связанных с коэффициентами сэмплирования.
|
||||||
|
|
||||||
#include <DB/Core/FieldVisitors.h>
|
#include <DB/Core/FieldVisitors.h>
|
||||||
#include <DB/Storages/MergeTree/MergeTreeDataSelectExecutor.h>
|
#include <DB/Storages/MergeTree/MergeTreeDataSelectExecutor.h>
|
||||||
#include <DB/Storages/MergeTree/MergeTreeBlockInputStream.h>
|
#include <DB/Storages/MergeTree/MergeTreeBlockInputStream.h>
|
||||||
#include <DB/Storages/MergeTree/MergeTreeReadPool.h>
|
#include <DB/Storages/MergeTree/MergeTreeReadPool.h>
|
||||||
#include <DB/Storages/MergeTree/MergeTreeThreadBlockInputStream.h>
|
#include <DB/Storages/MergeTree/MergeTreeThreadBlockInputStream.h>
|
||||||
#include <DB/Parsers/ASTIdentifier.h>
|
#include <DB/Parsers/ASTIdentifier.h>
|
||||||
|
#include <DB/Parsers/ASTSampleRatio.h>
|
||||||
#include <DB/DataStreams/ExpressionBlockInputStream.h>
|
#include <DB/DataStreams/ExpressionBlockInputStream.h>
|
||||||
#include <DB/DataStreams/FilterBlockInputStream.h>
|
#include <DB/DataStreams/FilterBlockInputStream.h>
|
||||||
#include <DB/DataStreams/CollapsingFinalBlockInputStream.h>
|
#include <DB/DataStreams/CollapsingFinalBlockInputStream.h>
|
||||||
@ -65,10 +68,13 @@ size_t MergeTreeDataSelectExecutor::getApproximateTotalRowsToRead(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Пожалуй, наиболее удобный способ разбить диапазон 64-битных целых чисел на интервалы по их относительной величине
|
using RelativeSize = boost::rational<ASTSampleRatio::BigNum>;
|
||||||
* - использовать для этого long double. Это некроссплатформенно. Надо, чтобы long double содержал хотя бы 64 бита мантиссы.
|
|
||||||
*/
|
static std::ostream & operator<<(std::ostream & ostr, const RelativeSize & x)
|
||||||
using RelativeSize = long double;
|
{
|
||||||
|
ostr << ASTSampleRatio::toString(x.numerator()) << "/" << ASTSampleRatio::toString(x.denominator());
|
||||||
|
return ostr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Переводит размер сэмпла в приблизительном количестве строк (вида SAMPLE 1000000) в относительную величину (вида SAMPLE 0.1).
|
/// Переводит размер сэмпла в приблизительном количестве строк (вида SAMPLE 1000000) в относительную величину (вида SAMPLE 0.1).
|
||||||
@ -77,8 +83,10 @@ static RelativeSize convertAbsoluteSampleSizeToRelative(const ASTPtr & node, siz
|
|||||||
if (approx_total_rows == 0)
|
if (approx_total_rows == 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
size_t absolute_sample_size = apply_visitor(FieldVisitorConvertToNumber<UInt64>(), typeid_cast<const ASTLiteral &>(*node).value);
|
const ASTSampleRatio & node_sample = typeid_cast<const ASTSampleRatio &>(*node);
|
||||||
return std::min(RelativeSize(1.0), RelativeSize(absolute_sample_size) / approx_total_rows);
|
|
||||||
|
auto absolute_sample_size = node_sample.ratio.numerator / node_sample.ratio.denominator;
|
||||||
|
return std::min(RelativeSize(1), RelativeSize(absolute_sample_size) / approx_total_rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -166,16 +174,18 @@ BlockInputStreams MergeTreeDataSelectExecutor::read(
|
|||||||
|
|
||||||
if (select.sample_size)
|
if (select.sample_size)
|
||||||
{
|
{
|
||||||
relative_sample_size = apply_visitor(FieldVisitorConvertToNumber<RelativeSize>(),
|
relative_sample_size.assign(
|
||||||
typeid_cast<ASTLiteral&>(*select.sample_size).value);
|
typeid_cast<const ASTSampleRatio &>(*select.sample_size).ratio.numerator,
|
||||||
|
typeid_cast<const ASTSampleRatio &>(*select.sample_size).ratio.denominator);
|
||||||
|
|
||||||
if (relative_sample_size < 0)
|
if (relative_sample_size < 0)
|
||||||
throw Exception("Negative sample size", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
throw Exception("Negative sample size", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||||
|
|
||||||
relative_sample_offset = 0;
|
relative_sample_offset = 0;
|
||||||
if (select.sample_offset)
|
if (select.sample_offset)
|
||||||
relative_sample_offset = apply_visitor(FieldVisitorConvertToNumber<RelativeSize>(),
|
relative_sample_offset.assign(
|
||||||
typeid_cast<ASTLiteral&>(*select.sample_offset).value);
|
typeid_cast<const ASTSampleRatio &>(*select.sample_offset).ratio.numerator,
|
||||||
|
typeid_cast<const ASTSampleRatio &>(*select.sample_offset).ratio.denominator);
|
||||||
|
|
||||||
if (relative_sample_offset < 0)
|
if (relative_sample_offset < 0)
|
||||||
throw Exception("Negative sample offset", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
throw Exception("Negative sample offset", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||||
@ -234,10 +244,8 @@ BlockInputStreams MergeTreeDataSelectExecutor::read(
|
|||||||
* <------> - size
|
* <------> - size
|
||||||
* <--><--> - кусочки для разных parallel_replica_offset, выбираем второй.
|
* <--><--> - кусочки для разных parallel_replica_offset, выбираем второй.
|
||||||
*
|
*
|
||||||
* TODO
|
|
||||||
* Очень важно, чтобы интервалы для разных parallel_replica_offset покрывали весь диапазон без пропусков и перекрытий.
|
* Очень важно, чтобы интервалы для разных parallel_replica_offset покрывали весь диапазон без пропусков и перекрытий.
|
||||||
* Также важно, чтобы весь юнивёрсум можно было покрыть, используя SAMPLE 0.1 OFFSET 0, ... OFFSET 0.9 и похожие десятичные дроби.
|
* Также важно, чтобы весь юнивёрсум можно было покрыть, используя SAMPLE 0.1 OFFSET 0, ... OFFSET 0.9 и похожие десятичные дроби.
|
||||||
* Сейчас это не гарантируется.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool use_sampling = relative_sample_size > 0 || settings.parallel_replicas_count > 1;
|
bool use_sampling = relative_sample_size > 0 || settings.parallel_replicas_count > 1;
|
||||||
@ -272,16 +280,16 @@ BlockInputStreams MergeTreeDataSelectExecutor::read(
|
|||||||
bool has_lower_limit = false;
|
bool has_lower_limit = false;
|
||||||
bool has_upper_limit = false;
|
bool has_upper_limit = false;
|
||||||
|
|
||||||
RelativeSize lower_limit_float = relative_sample_offset * size_of_universum;
|
RelativeSize lower_limit_rational = relative_sample_offset * size_of_universum;
|
||||||
RelativeSize upper_limit_float = (relative_sample_offset + relative_sample_size) * size_of_universum;
|
RelativeSize upper_limit_rational = (relative_sample_offset + relative_sample_size) * size_of_universum;
|
||||||
|
|
||||||
UInt64 lower = lower_limit_float;
|
UInt64 lower = boost::rational_cast<UInt64>(lower_limit_rational);
|
||||||
UInt64 upper = upper_limit_float;
|
UInt64 upper = boost::rational_cast<UInt64>(upper_limit_rational);
|
||||||
|
|
||||||
if (lower > 0)
|
if (lower > 0)
|
||||||
has_lower_limit = true;
|
has_lower_limit = true;
|
||||||
|
|
||||||
if (upper_limit_float <= size_of_universum)
|
if (lower_limit_rational <= size_of_universum)
|
||||||
has_upper_limit = true;
|
has_upper_limit = true;
|
||||||
|
|
||||||
/* std::cerr << std::fixed << std::setprecision(100)
|
/* std::cerr << std::fixed << std::setprecision(100)
|
||||||
|
Loading…
Reference in New Issue
Block a user