Длинная длинная арифметика без знака в двойную

Я делаю функцию, которая принимает 3 длинных длинных без знака и применяет закон косинусов, чтобы выяснить, является ли треугольник тупым, острым или прямоугольным. Должен ли я просто привести переменные к двойным значениям, прежде чем использовать их?

void triar( unsigned long long& r,
unsigned long long x,
unsigned long long y,
unsigned long long z )
{
if(x==0 || y==0 || z==0) die("invalid triangle sides");

double t=(x*x + y*y -z*z)/(2*x*y);

t=acos (t) * (180.0 / 3.14159265);

if(t > 90) {
cout<<"Obtuse Triangle"<<endl;
r=t;

} else if(t < 90){
cout<<"Acute Triangle"<<endl;
r=t;

} else if(t == 90){
cout<<"Right Traingle"<<endl;
r=t;

}
}

0

Решение

Как правило, нет причин, по которым вы не могли бы разыграть, если вам нужна арифметика с плавающей запятой. Тем не менее, есть также неявное преобразование из unsigned long в doubleТаким образом, вы также можете часто делать полностью без кастинга.

Во многих случаях, включая ваш, вы можете привести только один из аргументов, чтобы заставить double арифметика только на конкретной операции. Например,

double t = (double)(x*x + y*y - z*z) / (2*x*y)

Таким образом, все операции, кроме деления, вычисляются в целочисленной арифметике и поэтому выполняются немного быстрее. Приведение по-прежнему необходимо, чтобы избежать усечения во время деления.

Ваш код содержит сравнение аргументов с плавающей запятой. Арифметика с плавающей точкой, однако, почти неизбежно снижает точность. Избегайте ограниченной точности или анализируйте и контролируйте точность.

  • Предпочитайте целочисленное решение, как описано в превосходном ответе сестры, если в вашем распоряжении достаточно широкий целочисленный тип

  • Всегда избегайте перевода из радианов в градусы, кроме представления людям

  • Возьмите значение π из заголовочных файлов вашей математической библиотеки (к сожалению, это зависит от платформы — попробуйте _USE_MATH_DEFINES + M_PI или, если вы уже используете библиотеки повышения, boost::math::constants::pi<double>()) или выразить это аналитически. Например, std::atan(1)*2 это правильный угол.

  • Если вы выберете двойную точность, а предельная разница будет меньше, чем, скажем, std::numeric_limits<double>::min() * 8Вы, вероятно, ничего не можете сказать о треугольнике, и классификация, которую вы возвращаете, в основном фальшивая. (Я составил значение 8, вы, возможно, потеряете больше бит, чем три.)

2

Другие решения

У вас проблема с тупыми треугольниками, x*x + y*y - z*z математически даст отрицательный результат, который затем уменьшается по модулю 2^WIDTH (где WIDTH количество битов значения в unsigned long long, по крайней мере 64 и, вероятно, именно так), что дает — вероятно, большое — положительное значение (или в редких случаях 0). Тогда вычисленный результат t = (x*x + y*y - z*z)/(2*x*y) может быть больше 1 и acos(t) вернул бы NaN.

Правильный способ выяснить, является ли треугольник тупым / острым / прямоугольным с данным типом аргумента, это проверить, x*x + y*y < /* > / == */ z*z — если вы можете быть уверены, что математические результаты не превышают unsigned long long спектр.

Если вы не уверены в этом, вы можете преобразовать переменные в double до вычисления,

double xd = x, yd = y, zd = z;
double t = (xd*xd + yd*yd - zd*zd)/(2*xd*yd);

с возможной потерей точности и неправильными результатами для почти прямоугольных треугольников (например, для слегка тупого треугольника x = 2^29, y = 2^56-1, z = 2^56+2, и то и другое y а также z будет преобразован в 2 ^ 56 со стандартным 64-разрядным doubles, xd*xd + yd*yd = 2^58 + 2^112 будет оцениваться до 2^112вычитая zd*zd затем приводит к 0).

Или вы можете сравнить x*x + y*y в z*z — или же x*x в z*z - y*y — используя только целочисленную арифметику. Если x*x представима как unsigned long long (Я предполагаю, что 0 < x <= y <= z), это относительно просто, сначала проверьте, (z - y)*(z + y) будет превышать ULLONG_MAXесли да, то треугольник тупой, иначе посчитайте и сравните. Если x*x не представимо, это становится сложным, я думаю, что самый простой способ (за исключением использования большой целочисленной библиотеки, конечно) будет вычислять высокий и, при необходимости, низкий 64 (или любую другую ширину) unsigned long long имеет) биты отдельно, разделив числа на половину ширины и сравнив их.

Примечание: Ваше значение для π, 3.14159265 слишком неточно, прямоугольные треугольники будут отмечены как тупые.

1