Я работал над простой программой на C++ для аппроксимации числа Пи по формуле Белларда. Независимо от того, какое количество цифр он пытается вычислить, в результате получается число 3,145063.
Я попытался изменить его с хранения с плавающей запятой на двойную, потому что думал, что это может быть просто округление, чтобы соответствовать меньшему типу данных, но это все равно не работает.
Вот мой код:
#include <iostream>
#include <cmath>
double approximate(int digits) {
double summed = 0;
for (int n = 0; n < digits; n++) {
summed += (pow(-1, n) / pow(2, n * 10)) * ((-(pow(2, 5)) / (4 * n + 1)) - (1 / (4 * n + 3)) + (pow(2, 8) / (10 * n + 1)) - (pow(2, 6) / (10 * n + 3)) - (pow(2, 2) / (10 * n + 5)) - (pow(2, 2) / (10 * n + 7)) + (1 / (10 * n + 9)));
}
return (1 / pow(2, 6)) * summed;
}
int main() {
std::cout << "How many digits?: ";
std::string its;
std::getline(std::cin, its);
std::string approx = std::to_string(approximate(std::stoi(its)));
std::cout << approx << std::endl;
}
summed += -- Вместо этой ужасно длинной строки кода разбейте ее на несколько строк, чтобы увидеть, какое вычисление не соответствует вашим ожиданиям. И да, используйте отладчик.
1 / (10 * n + 9) — Это целочисленное деление. Целое число, разделенное на целое число, даст вам целое число. Но опять же, если бы вы нарушили эту линию, вы бы столкнулись с этой проблемой.
Примечание: компиляторы становятся умнее и могут уловить эту переборку, но pow(-1, n) переворачивать знаковый бит очень дорого, если компилятор не поймет это. pow — это отвратительная штука, предназначенная для борьбы с плохими матерями <ругательство удалено>, такими как вычисление числа Пи в степени е. Если вы немного переворачиваете pow(-1, n), немного двигаетесь pow(2,n) или выполняете простое возведение в квадрат pow(x,2), вы можете обнаружить, что программа выполняет гораздо больше работы, чем использование справочной таблицы, битового сдвига или умножения.
Примечание: опять же, компиляторы становятся умнее и могут заметить, что pow(2, 5) никогда не меняется, и кэшировать результат или, возможно, даже вычислить его во время компиляции и превратить в константу. Но проверь все равно.
И не забудьте проверить ввод на наличие ошибок: godbolt.org/z/jf6xv4nqT





Как уже упоминалось, если бы вы разбили строку на несколько операторов, вы бы увидели, что выполняете целочисленное деление вместо деления с плавающей запятой.
(1 / (4 * n + 3)) и
(1 / (10 * n + 9))
не верны. Простое исправление состоит в том, чтобы использовать 1.0, чтобы принудительно выполнить деление с плавающей запятой:
#include <iostream>
#include <cmath>
double approximate(int digits) {
double summed = 0;
for (int n = 0; n < digits; n++) {
summed += (pow(-1, n) / pow(2, n * 10)) *
((-(pow(2, 5)) / (4 * n + 1)) - (1.0 / (4 * n + 3)) + // <-- 1.0
(pow(2, 8) / (10 * n + 1)) -
(pow(2, 6) / (10 * n + 3)) -
(pow(2, 2) / (10 * n + 5)) -
(pow(2, 2) / (10 * n + 7)) + (1.0 / (10 * n + 9))); // <-- 1.0
}
return (1 / pow(2, 6)) * summed;
}
int main() {
std::string approx = std::to_string(approximate(10));
std::cout << approx << std::endl;
}
Выход:
3.141593
Другие вещи в вашем коде, которые также можно улучшить, а именно, вызовы pow. Вы знаете, что pow(2,2), pow(2,5), pow(2,8), pow(2,6), (1.0 / pow(2,6)), etc. are constants. They do not be need to be computed each and every time the дляloop iterates, or at theточки возврата. Просто создайте эти константы с плавающей запятой и используйте их.
Редактировать:
Вот версия вашего кода для времени компиляции, которая может вычислять с точностью до 6 цифр.
Обратите внимание, что выходные данные сборки нигде не содержат следов вызова функции approximate, поскольку все значение было вычислено во время компиляции.
Спасибо, это работает. Единственная проблема заключается в том, что результат, кажется, остается неизменным независимо от количества введенных цифр, но я думаю, что это совсем другая проблема.
@JakeStBu Помните, что алгоритм не вычисляет по одной цифре за раз, а гарантирует, что N цифр верны. Примерно после N=5 вы не увидите никакой разницы в вычислении первых 10 или около того цифр.
Похоже, вам, возможно, придется научиться использовать отладчик для пошагового выполнения кода. С помощью хорошего отладчика вы можете выполнять программу построчно и видеть, где она отклоняется от ожидаемого. Это важный инструмент, если вы собираетесь заниматься программированием. Дальнейшее чтение: Как отлаживать небольшие программы и Руководство по отладке