Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Глюки на тригонометрии при углах в 90n градусов.


Автор: Mal Hack 5.1.2006, 15:51
Код:
Код

 long double angle;
 angle = DegToRad( StrToFloat( MaskEdit1 -> Text ) );
....
 AngleGrid -> Cells[1][0] = FloatToStr( Sin( angle ) );
 AngleGrid -> Cells[1][1] = FloatToStr( Cos( angle ) );

При вводе имеем:
0: sin = 0, cos =1
90: sin = 1, cos = -2.7
180: sin = -5.4, cos = -1

К примеру на углах 45, 135, 405 все в норме.

Автор: BreakPointMAN 5.1.2006, 16:17
Что-то я не помню таких функций, как Sin и Cos... хмм...
А может, ошибка появляется при отображении? Ну, места там не хватает... или при преобразовании что-то случается...

Автор: _hunter 5.1.2006, 16:19
насколько я помню они радианы принимают... + что за странное название ( с большой буквы )? в math.h с маленькой...

Автор: Mal Hack 5.1.2006, 16:28
Цитата(_hunter @ 5.1.2006, 16:19 Найти цитируемый пост)

насколько я помню они радианы принимают... + что за странное название ( с большой буквы )? в math.h с маленькой...


Я в BDS 2006. Там есть Math.hpp и Math.h, из первой - с большой буквы, из второй - с маленькой. Но замена их на маленькие результата не меняет. По крайней мере в лучшую сторону.
Цитата(BreakPointMAN @ 5.1.2006, 16:17 Найти цитируемый пост)

А может, ошибка появляется при отображении? Ну, места там не хватает... или при преобразовании что-то случается...

Все преобразования приведены выше.
Вряд ли там места не хватает. На 45 все окей, как и в 0.
Добавлено @ 16:28
Цитата(_hunter @ 5.1.2006, 16:19 Найти цитируемый пост)

насколько я помню они радианы принимают...

Я им их и передаю. DegToRad преобразует. Преобразует правильно.
Проверялся так:
Код

 Edit5 -> Text =  FloatToStr( DegToRad( StrToFloat( MaskEdit1 -> Text ) ) );

Пишет нормальные значения PI, PI/2.

Автор: Illuminaty 5.1.2006, 16:29
а если напрямую написать?
Код

Sin(angle * PI / 180)

Автор: Mal Hack 5.1.2006, 16:40
Не помогает.

Автор: Illuminaty 5.1.2006, 16:44
а что выводит при явном задании значения переменной angle ?
Код

angle=180.0;

Автор: _hunter 5.1.2006, 16:57
стоп. все правтльно оно выдает.
при 180 син не -5.4 а -5.4 * 10^(-20) ( т.е. 0 )
напиши что-то типа AngleGrid -> Cells[1][0] = AnsiString().sprintf("%.3f", Sin( angle ) );

Автор: Partizan 5.1.2006, 17:14
У меня в BC++B6 результаты совершенно адекватные...
Код

long double angle;
 angle = DegToRad(180);
 AngleGrid -> Cells[1][0] = FloatToStr( sin( angle ) );
 AngleGrid -> Cells[1][1] = FloatToStr( cos( angle ) );


Выполнение этого кода даёт нам
sin: 1,2246...........E-16
cos: 1

Автор: Mal Hack 5.1.2006, 17:16
Цитата(_hunter @ 5.1.2006, 16:57 Найти цитируемый пост)

напиши что-то типа AngleGrid -> Cells[1][0] = AnsiString().sprintf("%.3f", Sin( angle ) );

"Что-то типа" не помогло. Помогло вот что:
Код

AngleGrid -> Cells[1][0] = FloatToStr( RoundTo( Sin( angle ) , -10 ) );


Огромное тебе спасибо !!!
Добавлено @ 17:16
Хотя каждый раз Round использовать не хочется smile

Автор: _hunter 5.1.2006, 17:22
а что значит не помогло?
минус мешает?
так его убрать можно...
или вообще так:
if ( abs(sin) < 0.00000000001 )
sin = 0;
написать
а потом sprintf ему...

Автор: Mal Hack 5.1.2006, 17:34
У косинуса при - был -0.000
Минус тут не при чем. Там 1 должна быть smile

Автор: _hunter 5.1.2006, 17:52
похоже sprintf() с long double-ами не работает. приводи перед конвертацией к doublе -- всеравно все знаки пользователю вряд ли нужны...
AnsiString().sprintf("%0.3f", (double)(Sin(DegToRad(angle))));

Автор: Mal Hack 5.1.2006, 20:29
_hunter сработало. Слу, а что лучше в плане быстродействия и грамотности кода?
RoundTo или через sprintf?

Автор: _hunter 6.1.2006, 11:19
интересный вопрос...
sprintf точно не меделеннее ( потому как при RounTo/FormatFloat выполняются аналогичные операции ) а возможно и быстрее -- новое значение не создается ( RounTo )
в любом случае нужно эксперимент ставить
а в полане грамотности ( а точнее красоты кода ) sprintf явно лучше

Автор: Mal Hack 6.1.2006, 15:04
_hunter а с точки зрения грамотности будет выгоднее вынести это преобразование в отдельную функцию? При условии, что мне надо меня формат, т.е. кол-во отображаемых знаков после запятой?

Автор: _hunter 6.1.2006, 15:15
в отдельную функцию смысла нет ( разве что читаемость немного повысить )
можно в том месте, где задается число знаков и формировать строку формата:
formatStr = "%0." + IntToStr(precision) + "f";
AnsiString().sprintf(formatStr, (double)(Sin(DegToRad(angle))));

Автор: Mal Hack 6.1.2006, 15:25
Окей. Спасибо. Так и сделаю.

Автор: Mal Hack 6.1.2006, 18:20
Хм, пишу вот так:
Код
 Prec = "%." +  IntToStr(PrecComboBox -> ItemIndex)+"f ";

Выдает:
Цитата
[C++ Error] Unit2.cpp(39): E2277 Lvalue required

или
Код
  Prec = "%." + IntToStr( PrecComboBox -> ItemIndex ) + "f";

Выдает:
Цитата
[C++ Error] [C++ Error] Unit2.cpp(269): E2085 Invalid pointer addition


Объявленно, как:
Код
char Prec[5];

Автор: Partizan 6.1.2006, 18:33
Потому что слева у тебя char...а справа AnsiString...

Писать нужно так:
Код

  Prec = ("%." + IntToStr( PrecComboBox -> ItemIndex ) + "f").c_str();

или так...
Код

  Prec = AnsiString("%." + IntToStr( PrecComboBox -> ItemIndex ) + "f").c_str();

Либо вообще так:
Код

  AnsiString Prec;
  Prec = "%." + IntToStr( PrecComboBox -> ItemIndex ) + "f";
//и уже дальше
 sprintf(Prec.c_str(), ...., ...);

Автор: Mal Hack 6.1.2006, 18:43
Цитата(Partizan @ 6.1.2006, 18:33 Найти цитируемый пост)

Либо вообще так:

Это в моем случае получается несколько не рационально.
Воспользовался вариантом:
Цитата(Partizan @ 6.1.2006, 18:33 Найти цитируемый пост)

  Prec = AnsiString("%." + IntToStr( PrecComboBox -> ItemIndex ) + "f").c_str();

Эх, никак не могу привыкнуть, что AnsiString это не простая строка, а целый класс...

Спасибо.
Добавлено @ 18:43
Стоп. Выдает все равно [C++ Error] Unit2.cpp(39): E2277 Lvalue required

Автор: Partizan 6.1.2006, 18:49
а...ну дык пральна...
если уж ты остановился на том варианте переопредели Prec так
Код

char *Prec;

Автор: _hunter 6.1.2006, 18:56
+ не понятно чем AnsiString не нравится. почему бы ее и не использовать?

Автор: Mal Hack 6.1.2006, 19:25
Цитата(_hunter @ 6.1.2006, 18:56 Найти цитируемый пост)

+ не понятно чем AnsiString не нравится. почему бы ее и не использовать?

Когда подставляю в формат, мне пишет несоответствие типов cont char * и AnsiString.
Цитата(Partizan @ 6.1.2006, 18:49 Найти цитируемый пост)

если уж ты остановился на том варианте переопредели Prec так

Пробовал.
Ругается вот так: [C++ Error] Unit2.cpp(39): E2235 Member function must be called or its address taken
Т.е. мы как-то не правильно в указатель пишем.

Автор: Partizan 6.1.2006, 19:58
Цитата

Цитата(_hunter @  6.1.2006,  18:56 Найти цитируемый пост)

+ не понятно чем AnsiString не нравится. почему бы ее и не использовать?

Когда подставляю в формат, мне пишет несоответствие типов cont char * и AnsiString.

когда подставляешь в формат не забывай вызывать !!!c_str()!!!
Prec.c_str() - это у тебя должно быть в формате в случае AnsiString Prec;

Цитата
Цитата(Partizan @  6.1.2006,  18:49 Найти цитируемый пост)

если уж ты остановился на том варианте переопредели Prec так

Пробовал.
Ругается вот так: [C++ Error] Unit2.cpp(39): E2235 Member function must be called or its address taken
Т.е. мы как-то не правильно в указатель пишем.

гммм...щас посмотрим...
E2235 Member function must be called or its address taken
A reference to a member function must be called, or its address must be taken with & operator....
возможно ты опечатался...
лично у меня следующий тест проходит на ура:
Код

char *Prec;
float MyVar = 1.099999;

Prec = ("%." + IntToStr( 2 ) + "f").c_str();

char *Buffer = new char[strlen(Prec)];

sprintf(Buffer, Prec, MyVar );
ShowMessage(Buffer);

delete[] Buffer;


Автор: Mal Hack 6.1.2006, 20:15
Цитата(Partizan @ 6.1.2006, 19:58 Найти цитируемый пост)

Prec = ("%." + IntToStr( 2 ) + "f").c_str();

Вот тут выкидывает на таком же коде.

Автор: TIGERоX 6.1.2006, 22:12
используя ансистринг
Код

  int intPrec = 2;//точность
  double value = 1.099999;
  AnsiString ansiPrec = "%." + IntToStr( intPrec ) + "f";
  AnsiString rez;//результат тут
  rez.sprintf(ansiPrec.c_str(),value);

если не нравится замени на char
Код

  int intPrec = 2;
  double value = 1.099999;
  char *charPrec = ("%." + IntToStr( intPrec ) + "f").c_str();
  AnsiString rez;
  rez.sprintf(charPrec,value);

Все должно ботать

Автор: Mal Hack 6.1.2006, 22:51
Цитата(TIGERоX @ 6.1.2006, 22:12 Найти цитируемый пост)

  char *charPrec = ("%." + IntToStr( intPrec ) + "f").c_str();

Не катит. Таже фигня.
Может это уже особенности BDS, но факт остается фактом.
Цитата(TIGERоX @ 6.1.2006, 22:12 Найти цитируемый пост)

используя ансистринг

Такой вариант работает, но не хочу я каждый раз вызывать лишнюю функцию.
В общем, сделал вот так:
Код

 *Prec = * ("%." + IntToStr( PrecComboBox -> ItemIndex ) + "f").c_str();

Добавлено @ 22:39
Блин, а так AccessViolation выкидывает smile
Добавлено @ 22:51
Код

 Prec = ("%." + IntToStr( PrecComboBox -> ItemIndex ) + "f").c_str();

- Компилится, но когда подставляю формат, появляются какие-то квадраты smile

Автор: BreakPointMAN 6.1.2006, 23:14
НУ МАТЬ МОЯ ЖЕНЩИНА!..
Код

Prec = ("%." + IntToStr( PrecComboBox -> ItemIndex ) + "f").c_str();

Нельзя, блин, так, блин, делать... блин... ибо "("%." + IntToStr( PrecComboBox -> ItemIndex ) + "f")" это временный объект... Нельзя взять его char*-представление, и потом использовать этот адрес для инициализации указателя... Временный объект будет разрушен после выполнения этой строки, и по указателю фиг знает что будет лежать, блин.

Автор: Partizan 6.1.2006, 23:16
А ты весь код приведи....вот и разберёмся...

Автор: Mal Hack 6.1.2006, 23:27
Цитата(BreakPointMAN @ 6.1.2006, 23:14 Найти цитируемый пост)

Нельзя, блин, так, блин, делать... блин... ибо "("%." + IntToStr( PrecComboBox -> ItemIndex ) + "f")" это временный объект... Нельзя взять его char*-представление, и потом использовать этот адрес для инициализации указателя... Временный объект будет разрушен после выполнения этой строки, и по указателю фиг знает что будет лежать, блин.

Сразу надо было говорить smile

Сделал так:
Код

 AnsiString PrecStr = "%." + IntToStr( PrecComboBox -> ItemIndex ) + "f";
 Prec = PrecStr.c_str();

Автор: Mal Hack 7.1.2006, 00:15
Так, вроде бы со всем разобрался...
Но есть такой глюк.
Есть несколько вот таких функций, висящих на разных кнопках и изменяющих разные Edit'ы. (*)
Код

void __fastcall TForm2::ExpBtnClick(TObject *Sender)
{
 try
  {  ExpEditRes -> Text = AnsiString().sprintf( Prec, (double) Exp( StrToFloat( ExpEditPower -> Text ) ) );  }
 catch ( Exception& )
  {  Application -> MessageBox( "Слишком большая степень." , "Ошибка" , MB_OK );  }
}

Есть список, из 9 элементов, котоый дает уже знакомую точность. Вот у него идет обраюотка на OnChange.
Вот функция
Код

void __fastcall TForm2::PrecComboBoxChange(TObject *Sender)
{
 AnsiString PrecStr = "%0." + IntToStr( PrecComboBox -> ItemIndex ) + "f";
 Prec = PrecStr.c_str();
 Form2->Caption = Prec;

 AngleBtnClick( Sender );
 LogBtnClick( Sender );
 ExpBtnClick( Sender );
}

Как видно, она вызывает вот те самые функции (*)
Так вот, когда я выбираю, все значения в этих функциях изменяют значения при выводе на новую точность. Все это работает.
Но, вот если я выбрал точность, начиная с 4 ItemIndex или %0.3f, то если нажать на кнопку, все поля, которые должны пересчитаться - обнуляются. Why?
Внутри функций Prec используется только как:
Код
  LogEdit10 -> Text = AnsiString().sprintf( Prec, (double) Log10( x ) );

Т.е. Prec не ПЕРЕОПРЕДЕЛЯЕТСЯ в них.

Автор: _hunter 10.1.2006, 12:01
странно. по коду граблей не видно... нужно отладчиком походить.

а что будет если так:
LogEdit10 -> Text = AnsiString().sprintf("%0.3f", (double) Log10( x ) );
написать?

Автор: Mal Hack 10.1.2006, 19:01
Так все остается, как надо.
Странно, но Prec изменяет свое значение при клике на кнопки. Почему? Фиг знает, ведь она там не изменяется.

Я так чувствую, что где-то я очень глупо указатель использую.
Вот прикладываю исходник.

Автор: teesync 27.7.2022, 05:10
Модератор: Сообщение скрыто.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)