Модераторы: Partizan, gambit
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Округление чисел, Округление чисел, типы данных 
V
    Опции темы
WOoHOo
Дата 27.7.2013, 10:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 24
Регистрация: 30.11.2010

Репутация: нет
Всего: нет



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

Проблема заключается в том, что не совпадают сгенерированные километражи и общий реальный (высчитанный: всего километров = (Литров всего потрачено * 100 км) / Расход топлива машины). Хотя нюансы были учтены. Всего скорее проблема с типами в функции CalcLiters(), но я в упор не вижу что там не так >_<

Скриншот программы: http://gyazo.com/634bbecd03541507c46fdff7478901f2

Код ниже (приложение WPF).

Код

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.Data;

namespace wpf_Calc
{
    /// <summary>
    /// Логика взаимодействия для MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        double full_km = 0;             // Километраж всех поездок 
        DataTable dt_Rows;  

        // Конструктор класса
        public MainWindow()
        {
            InitializeComponent();

            /* Инициализация элементов управления формы */

            // Button
            this.button_Generate.Click += new RoutedEventHandler(button_Generate_Click);

            // TextBox
            textBox_Fuel.Text = "9,6";
            textBox_Count.Text = "7";
            textBox_Liters.Text = "80";
           
            // DataTable
            dt_Rows = new DataTable();
            dt_Rows.TableName = "table";

            DataColumn number = new DataColumn();
            number.ColumnName = "№";
            number.DataType = typeof(int);
            number.AllowDBNull = false;
            dt_Rows.Columns.Add(number);

            DataColumn kilometers = new DataColumn();
            kilometers.ColumnName = "Километраж";
            kilometers.DataType = typeof(int);
            kilometers.AllowDBNull = false;
            dt_Rows.Columns.Add(kilometers);

            DataColumn liters = new DataColumn();
            liters.ColumnName = "Расход";
            liters.DataType = typeof(double);
            liters.AllowDBNull = true;
            dt_Rows.Columns.Add(liters);
            
            // DataGrid
            DataGridTextColumn col1 = new DataGridTextColumn();
            col1.Header = "№ выезда";
            col1.Width = 70;
            col1.Binding = new Binding("[0]");
            this.dataGrid.Columns.Add(col1);

            DataGridTextColumn col2 = new DataGridTextColumn();
            col2.Header = "Километров пройдено";
            col2.Width = 140;
            col2.Binding = new Binding("[1]");
            this.dataGrid.Columns.Add(col2);

            DataGridTextColumn col3 = new DataGridTextColumn();
            col3.Header = "Расход литров";
            col3.Width = 100;
            col3.Binding = new Binding("[2]");
            this.dataGrid.Columns.Add(col3);
            
            this.dataGrid.Width = 320;
            this.dataGrid.ItemsSource = dt_Rows.DefaultView; 
        }

        // Реакция на нажатие кнопки "Сгенерировать"
        private void button_Generate_Click(object sender, EventArgs e)
        {
            // Вычисляем сколько км было пройдено за все поездки
            // х = (Литров всего потрачено * 100 км) / Расход топлива машины
            full_km = double.Parse(textBox_Liters.Text) * 100 / double.Parse(textBox_Fuel.Text);
            label4.Content = "Километраж на " + textBox_Liters.Text + " литров составляет " + Math.Round(full_km) + " км.";

            // Вычисляем средний километраж 1 поездки 
            // x = Общий километраж / Количество выездов
            double middle_km = full_km / (double.Parse(textBox_Count.Text));
            label5.Content = "Средний километраж 1 поездки: " + middle_km + " км.";

            // Для правдивости данных поездки (километраж) - делаем их разными
            // Вычисляем разброс по километражу за 1 поездку (километраж поездок отличается друг от друга не более чем на 10%)
            // x = 10% Cреднего значения километража 1 поездки
            double dispersion = middle_km * (double)0.1;
            
            Random rnd = new Random((int)DateTime.Now.Ticks);

            // Генерация километража для всех поездок
            for (int i = 0; i < Int32.Parse(textBox_Count.Text); i++)
            {
                // Генерируем новое значение километража для следующей поездки
                FillKilometers(i, rnd.Next(Convert.ToInt32(Math.Round(middle_km - dispersion)), Convert.ToInt32(Math.Round(middle_km + dispersion))));
            }

            // После того как значения километража сгенерированы - проверяем их сумму и высчитываем для них расход топлива
            CalcLiters();
 
            // Контроль километража
            int end_km = 0;
            for (int i = 0; i < dt_Rows.Rows.Count; i++)
            {
                 DataRow row = dt_Rows.Rows[i];
                 end_km = end_km + Convert.ToInt32(row["Километраж"]);
            }
            MessageBox.Show("Проверка. В сумме километров сгенерировано:  " + end_km.ToString());
            
            // Контроль литров
            double end_liters = 0;
            for (int i = 0; i < dt_Rows.Rows.Count; i++)
            {
                 DataRow row = dt_Rows.Rows[i];
                 end_liters = end_liters + Convert.ToDouble(row["Расход"]);
            }
            MessageBox.Show("Проверка. В сумме литров сгенерировано: " + end_liters.ToString());
        }

        // Заполнение DataTable сгенерированными значениями километража
        private void FillKilometers(int num, int kilometers)
        {
            DataRow row = dt_Rows.NewRow();
            row["№"] = num;
            row["Километраж"] = kilometers;
            dt_Rows.Rows.Add(row);
        }

        // Расчет расхода топлива по каждой поездке
        private void CalcLiters()
        {
            int full_gen_km = 0; // Общий сгенерированный километраж
            // Вычисление общего километража сгенерированного программой
            for (int i = 0; i < dt_Rows.Rows.Count; i++)
            {
                full_gen_km = full_gen_km + Convert.ToInt32(dt_Rows.Rows[i]["Километраж"]);
            }
            MessageBox.Show("Сгенерировано км.: " + full_gen_km + " Разница: " + (full_km - full_gen_km));

            // Если реальный километраж больше сгенерированного
            if (full_km > full_gen_km) 
            {
                // Вычисляем среднее значение недостающих километров
                // х = (Реальный километраж - Сгенерированный километраж) / Количество выездов
                double add = Math.Round(full_km - full_gen_km) / int.Parse(textBox_Count.Text);
                MessageBox.Show("Всего прибавить: " + (full_km - full_gen_km) + " В среднем прибавка составляет: " + add.ToString());

                // К каждому сгенерированному километражу прибавляем недостающие километры           
                for (int i = 0; i < dt_Rows.Rows.Count; i++)
                {
                    DataRow row = dt_Rows.Rows[i];
                    row["Километраж"] = Convert.ToInt32(row["Километраж"]) + Math.Round(add);
                    row["Расход"] = Math.Round(Convert.ToDouble(row["Километраж"]) / double.Parse(textBox_Fuel.Text), 2);
                }
            }
            else // Отнимаем
            {
                double subtract = Math.Round(full_km - full_gen_km) / double.Parse(textBox_Count.Text);
                MessageBox.Show("Отбавка составляет: " + subtract.ToString());
                // от каждого эменета отнимает приб
                for (int i = 0; i < dt_Rows.Rows.Count; i++)
                {
                    DataRow row = dt_Rows.Rows[i];
                    row["Километраж"] = Convert.ToInt32(row["Километраж"]) + Math.Round(subtract);
                    row["Расход"] = Math.Round(Convert.ToDouble(row["Километраж"]) / double.Parse(textBox_Fuel.Text), 2);
                }
            }
            dataGrid.Items.Refresh();
        }
    }
}
 

Код

<Window x:Class="wpf_Calc.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Генератор расхода топлива" Height="564" Width="385">
    <Grid Height="515">
        <Label Content="Расход топлива:" Height="28" HorizontalAlignment="Left" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" />
        <Label Content="Количество выездов:" Height="28" HorizontalAlignment="Left" Margin="12,46,0,0" Name="label2" VerticalAlignment="Top" />
        <Label Content="Литров потрачено:" Height="28" HorizontalAlignment="Left" Margin="12,77,0,0" Name="label3" VerticalAlignment="Top" />
        <Label Content="Километров пройдено:" Height="28" HorizontalAlignment="Left" Margin="12,111,0,0" Name="label4" VerticalAlignment="Top" />
        <Label Content="Средний километраж 1 поездки:" Height="28" HorizontalAlignment="Left" Margin="20,143,0,0" Name="label5" VerticalAlignment="Top" />
        
        
        <TextBox Height="23" HorizontalAlignment="Left" Margin="156,12,0,0" Name="textBox_Fuel" VerticalAlignment="Top" Width="120" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="156,51,0,0" Name="textBox_Count" VerticalAlignment="Top" Width="120" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="156,79,0,0" Name="textBox_Liters" VerticalAlignment="Top" Width="120" />
        
        <Button Content="Расчитать" Height="23" HorizontalAlignment="Left" Margin="20,177,0,0" Name="button_Generate" VerticalAlignment="Top" Width="75" Foreground="Red" FontSize="11pt"/>
        
        <DataGrid AutoGenerateColumns="False" Height="307" HorizontalAlignment="Left" Margin="20,206,0,0" Name="dataGrid" VerticalAlignment="Top" Width="327"></DataGrid>
       
    </Grid>
</Window>


Также принимается критика по коду, предложения по улучшению и прочее.   smile 

PM MAIL   Вверх
CosmoMan
Дата 29.7.2013, 13:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 110
Регистрация: 12.7.2005
Где: Харьков

Репутация: нет
Всего: 0



Честно, не понял где у вас "сгенерированные километражи" и "общий реальный" и чем он отличается. 

Подозрительная строка где может быть потеря точности. 
Код

double add = Math.Round(full_km - full_gen_km) / int.Parse(textBox_Count.Text);


По коду лучше все константы вроде "Километраж" или "Расход" выносить в переменные. 
И имя переменных не очень информативно. 
CalcLiters() лучше назвать CalcFuelSpend()
PM MAIL   Вверх
WOoHOo
Дата 29.7.2013, 22:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 24
Регистрация: 30.11.2010

Репутация: нет
Всего: нет



Цитата(CosmoMan @  29.7.2013,  13:40 Найти цитируемый пост)
Честно, не понял где у вас "сгенерированные километражи" и "общий реальный" и чем он отличается. 


Общий реальный километраж (сколько действительно проехали все машины, если на них всех выдать N литров бензина). Расчитывается исходя из веденных данных пользователем. 
Формула: общий километраж = (Литров всего потрачено * 100 км) / Расход топлива машины
Код:
Код

private void button_Generate_Click(object sender, EventArgs e)
        {
            // Вычисляем сколько км было пройдено за все поездки
            // х = (Литров всего потрачено * 100 км) / Расход топлива машины
            full_km = double.Parse(textBox_Liters.Text) * 100 / double.Parse(textBox_Fuel.Text);
            label4.Content = "Километраж на " + textBox_Liters.Text + " литров составляет " + Math.Round(full_km) + " км.";

            ...
         }

Частный сгенерированный километраж (на каждую поездку). После определения общего километража возникает вопрос: а сколько в каждую машну вливать бензина? Для того, чтобы понять сколько на машину выделить бензина, для этого необходим километраж. Делим общий километраж на количество поездок - получаем сколько в среднем проезжает машина. Для того, чтобы данные не были "палевными" высчитываем дисперсию (разницу) в +-10% от среднего значения поездки. И в этом интервале генерируем значение километража Генерируется стандартной функцией Random() . Естественно рэндом не может нам нагенерировать значение километражей, чтобы их сумма в тютельку совпадала с реальным. Сумма нагенерированных значений будет либо > либо < реального значения. Поэтому переходим к следующему пункту.
Код:
Код

// Реакция на нажатие кнопки "Сгенерировать"
        private void button_Generate_Click(object sender, EventArgs e)
        {
          ...

            // Вычисляем средний километраж 1 поездки 
            // x = Общий километраж / Количество выездов
            double middle_km = full_km / (double.Parse(textBox_Count.Text));
            label5.Content = "Средний километраж 1 поездки: " + middle_km + " км.";

            // Для правдивости данных поездки (километраж) - делаем их разными
            // Вычисляем разброс по километражу за 1 поездку (километраж поездок отличается друг от друга не более чем на 10%)
            // x = 10% Cреднего значения километража 1 поездки
            double dispersion = middle_km * (double)0.1;
            
            Random rnd = new Random((int)DateTime.Now.Ticks);

            // Генерация километража для всех поездок
            for (int i = 0; i < Int32.Parse(textBox_Count.Text); i++)
            {
                // Генерируем новое значение километража для следующей поездки
                FillKilometers(i, rnd.Next(Convert.ToInt32(Math.Round(middle_km - dispersion)), Convert.ToInt32(Math.Round(middle_km + dispersion))));
            }
          ...
}


Общий сгенерированный километраж. Здесь проверяем сумму нагенерированных значений. Получается, что сумма = общий нагенерированный километраж всех поездок. Эту сумму сравниваем с реальным километражом. Если нагенерировали больше - надо отнять N число от каждого километража поездок, если нагенерировали меньше - соответственно прибавить недостающие километры.  После того, как реальный и сгенерированный выправленный километражи совпадут - можно высчитывать расход бензина. 
Код

// Расчет расхода топлива по каждой поездке
        private void CalcLiters()
        {
            int full_gen_km = 0; // Общий сгенерированный километраж
            // Вычисление общего километража сгенерированного программой
            for (int i = 0; i < dt_Rows.Rows.Count; i++)
            {
                full_gen_km = full_gen_km + Convert.ToInt32(dt_Rows.Rows[i]["Километраж"]);
            }
            MessageBox.Show("Сгенерировано км.: " + full_gen_km + " Разница: " + (full_km - full_gen_km));

            // Если реальный километраж больше сгенерированного
            if (full_km > full_gen_km) 
            {
                // Вычисляем среднее значение недостающих километров
                // х = (Реальный километраж - Сгенерированный километраж) / Количество выездов
                double add = Math.Round(full_km - full_gen_km) / int.Parse(textBox_Count.Text);
                MessageBox.Show("Всего прибавить: " + (full_km - full_gen_km) + " В среднем прибавка составляет: " + add.ToString());

                // К каждому сгенерированному километражу прибавляем недостающие километры           
                for (int i = 0; i < dt_Rows.Rows.Count; i++)
                {
                    DataRow row = dt_Rows.Rows[i];
                    row["Километраж"] = Convert.ToInt32(row["Километраж"]) + Math.Round(add);
                    row["Расход"] = Math.Round(Convert.ToDouble(row["Километраж"]) / double.Parse(textBox_Fuel.Text), 2);
                }
            }
            else // Отнимаем
            {
                double subtract = Math.Round(full_km - full_gen_km) / double.Parse(textBox_Count.Text);
                MessageBox.Show("Отбавка составляет: " + subtract.ToString());
                // от каждого эменета отнимает приб
                for (int i = 0; i < dt_Rows.Rows.Count; i++)
                {
                    DataRow row = dt_Rows.Rows[i];
                    row["Километраж"] = Convert.ToInt32(row["Километраж"]) + Math.Round(subtract);
                    row["Расход"] = Math.Round(Convert.ToDouble(row["Километраж"]) / double.Parse(textBox_Fuel.Text), 2);
                }
            }
            dataGrid.Items.Refresh();
        }

Проблема, как раз и заключается в том, что не смотря на все корректировки общего сгенерированного километража, он не совпадает с реальным.  В результате неправильно высчитываются и литры (расход).

Код

double add = Math.Round(full_km - full_gen_km) / int.Parse(textBox_Count.Text);

По мне тут все ровно. Полные километражи full_km - всегда в дабл были, я их не округлял. full_gen_km - int. Random генерирует только целые. Тут потери нет. Число поездок тоже целое - textBox_Count, 1.5 поездки не может быть. Далее add используется вроде правильно...

PM MAIL   Вверх
CosmoMan
Дата 1.8.2013, 14:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 110
Регистрация: 12.7.2005
Где: Харьков

Репутация: нет
Всего: 0



Я вижу тут две проблемы. 

1. У вас сумма в столбике "Километры"  = 834. Это всего на 1 л больше, чем расчетная величина. Видимо потому, что ф-ция Math.Round округляет по правилам округления в большую сторону от 0.5.
Т.к. вы округляете литры до целого везде, где только можно, у вас накапливается ошибка, которая может увеличиваться по мере роста числа поездок. 
Я в своих расчетах не использовал округление вовсе и у меня сумма в столбике "Километры" точно равна 833.33. 

2. Вы вычисляете Расход по формуле = км. пройдено / расход топлива (л/км). 
Но расход топлива это величина Литров / 100 км.
Эта формула выдаст вам Расход топливи в литрах на км. пройдено.
Т.е. если расход 9.6 на 100 км, то вы получите для первого 13.12 литров на 126 км = 9.6 л на 100 км. В этой величине нет смысла. Это просто отношение = расходу на 100 км.

Если вам нужно почитать величину в ЛИТРАХ, которую потратил автомобиль за каждую поездку, нужно использовать пропорцию. 
потрачено литров = км. пройдено * расход / 100

Тогда сумма расхода в литрах будет точно совпадать с суммарным числом потраченных литров (80 в примере) 

Я прикрепил эксель документ с моими расчетами, может быть это что то прояснит. 



Это сообщение отредактировал(а) CosmoMan - 1.8.2013, 14:02

Присоединённый файл ( Кол-во скачиваний: 2 )
Присоединённый файл  FuelSpendingCounter.zip 10,21 Kb
PM MAIL   Вверх
WOoHOo
Дата 1.8.2013, 22:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 24
Регистрация: 30.11.2010

Репутация: нет
Всего: нет



CosmoMan, приветствую. 

Спасибо за советы, пригодились. В итоге проблема побеждена: решил расчитывать и выводить километры в double (как у вас в файлике), чтобы не было расхождений по десятым, которые как и сказали - сыграли свою роль (разницу в 1-2 километра), убрал Math.Round; и таки да - проблема была в переменной add, с ее прибавлением и округлением; поправил генерацию чисел - перевел на double.

Еще раз спасибо, что отважились влезть в математические дебри. 

[РЕШЕНО]

PM MAIL   Вверх
CosmoMan
Дата 2.8.2013, 13:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 110
Регистрация: 12.7.2005
Где: Харьков

Репутация: нет
Всего: 0



Цитата(WOoHOo @  1.8.2013,  22:08 Найти цитируемый пост)
Еще раз спасибо, что отважились влезть в математические дебри. 


Незачто) Удачи вам в вашей работе. 
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
mr.DUDA
THandle

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.
Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :)
Так же не забывайте отмечать свой вопрос решенным, если он таковым является :)


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема »


 




[ Время генерации скрипта: 0.0919 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.